disable autoplay within messages and trim feelers (#5260)

zio/stable
Samuel Newman 2024-09-11 16:20:32 +01:00 committed by GitHub
parent b04ecbe54d
commit 580b67ba37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 50 additions and 25 deletions

View File

@ -0,0 +1,17 @@
import React from 'react'
const MessageContext = React.createContext(false)
export function MessageContextProvider({
children,
}: {
children: React.ReactNode
}) {
return (
<MessageContext.Provider value={true}>{children}</MessageContext.Provider>
)
}
export function useIsWithinMessage() {
return React.useContext(MessageContext)
}

View File

@ -4,6 +4,7 @@ import {AppBskyEmbedRecord} from '@atproto/api'
import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds' import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds'
import {atoms as a, native, useTheme} from '#/alf' import {atoms as a, native, useTheme} from '#/alf'
import {MessageContextProvider} from './MessageContext'
let MessageItemEmbed = ({ let MessageItemEmbed = ({
embed, embed,
@ -13,13 +14,15 @@ let MessageItemEmbed = ({
const t = useTheme() const t = useTheme()
return ( return (
<View style={[a.my_xs, t.atoms.bg, native({flexBasis: 0})]}> <MessageContextProvider>
<PostEmbeds <View style={[a.my_xs, t.atoms.bg, native({flexBasis: 0})]}>
embed={embed} <PostEmbeds
allowNestedQuotes embed={embed}
viewContext={PostEmbedViewContext.Feed} allowNestedQuotes
/> viewContext={PostEmbedViewContext.Feed}
</View> />
</View>
</MessageContextProvider>
) )
} }
MessageItemEmbed = React.memo(MessageItemEmbed) MessageItemEmbed = React.memo(MessageItemEmbed)

View File

@ -11,6 +11,7 @@ import {useAutoplayDisabled} from 'state/preferences'
import {VideoEmbedInnerNative} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative' import {VideoEmbedInnerNative} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import {useIsWithinMessage} from '#/components/dms/MessageContext'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
import {VisibilityView} from '../../../../../modules/expo-bluesky-swiss-army' import {VisibilityView} from '../../../../../modules/expo-bluesky-swiss-army'
@ -68,7 +69,8 @@ function InnerWrapper({embed}: Props) {
const [isMuted, setIsMuted] = useState(player.muted) const [isMuted, setIsMuted] = useState(player.muted)
const [isFullscreen, setIsFullscreen] = React.useState(false) const [isFullscreen, setIsFullscreen] = React.useState(false)
const [timeRemaining, setTimeRemaining] = React.useState(0) const [timeRemaining, setTimeRemaining] = React.useState(0)
const disableAutoplay = useAutoplayDisabled() const isWithinMessage = useIsWithinMessage()
const disableAutoplay = useAutoplayDisabled() || isWithinMessage
const isActive = embed.playlist === activeSource && activeViewId === viewId const isActive = embed.playlist === activeSource && activeViewId === viewId
// There are some different loading states that we should pay attention to and show a spinner for // There are some different loading states that we should pay attention to and show a spinner for
const isLoading = const isLoading =

View File

@ -12,6 +12,7 @@ import {
VideoNotFoundError, VideoNotFoundError,
} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb' } from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {useIsWithinMessage} from '#/components/dms/MessageContext'
import {useFullscreen} from '#/components/hooks/useFullscreen' import {useFullscreen} from '#/components/hooks/useFullscreen'
import {ErrorBoundary} from '../ErrorBoundary' import {ErrorBoundary} from '../ErrorBoundary'
import {useActiveVideoWeb} from './ActiveVideoWebContext' import {useActiveVideoWeb} from './ActiveVideoWebContext'
@ -42,6 +43,16 @@ export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) {
return () => observer.disconnect() return () => observer.disconnect()
}, [sendPosition, isFullscreen]) }, [sendPosition, isFullscreen])
// In case scrolling hasn't started yet, send up the position
const isAnyViewActive = currentActiveView !== null
useEffect(() => {
if (ref.current && !isAnyViewActive) {
const rect = ref.current.getBoundingClientRect()
const position = rect.y + rect.height / 2
sendPosition(position)
}
}, [isAnyViewActive, sendPosition])
const [key, setKey] = useState(0) const [key, setKey] = useState(0)
const renderError = useCallback( const renderError = useCallback(
(error: unknown) => ( (error: unknown) => (
@ -73,9 +84,7 @@ export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) {
style={{display: 'flex', flex: 1, cursor: 'default'}} style={{display: 'flex', flex: 1, cursor: 'default'}}
onClick={evt => evt.stopPropagation()}> onClick={evt => evt.stopPropagation()}>
<ErrorBoundary renderError={renderError} key={key}> <ErrorBoundary renderError={renderError} key={key}>
<ViewportObserver <ViewportObserver sendPosition={sendPosition}>
sendPosition={sendPosition}
isAnyViewActive={currentActiveView !== null}>
<VideoEmbedInnerWeb <VideoEmbedInnerWeb
embed={embed} embed={embed}
active={active} active={active}
@ -96,15 +105,14 @@ export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) {
function ViewportObserver({ function ViewportObserver({
children, children,
sendPosition, sendPosition,
isAnyViewActive,
}: { }: {
children: React.ReactNode children: React.ReactNode
sendPosition: (position: number) => void sendPosition: (position: number) => void
isAnyViewActive?: boolean
}) { }) {
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const [nearScreen, setNearScreen] = useState(false) const [nearScreen, setNearScreen] = useState(false)
const [isFullscreen] = useFullscreen() const [isFullscreen] = useFullscreen()
const isWithinMessage = useIsWithinMessage()
// Send position when scrolling. This is done with an IntersectionObserver // Send position when scrolling. This is done with an IntersectionObserver
// observing a div of 100vh height // observing a div of 100vh height
@ -126,25 +134,18 @@ function ViewportObserver({
return () => observer.disconnect() return () => observer.disconnect()
}, [sendPosition, isFullscreen]) }, [sendPosition, isFullscreen])
// In case scrolling hasn't started yet, send up the position
useEffect(() => {
if (ref.current && !isAnyViewActive) {
const rect = ref.current.getBoundingClientRect()
const position = rect.y + rect.height / 2
sendPosition(position)
}
}, [isAnyViewActive, sendPosition])
return ( return (
<View style={[a.flex_1, a.flex_row]}> <View style={[a.flex_1, a.flex_row]}>
{nearScreen && children} {nearScreen && children}
<div <div
ref={ref} ref={ref}
style={{ style={{
// Don't escape bounds when in a message
...(isWithinMessage
? {top: 0, height: '100%'}
: {top: 'calc(50% - 50vh)', height: '100vh'}),
position: 'absolute', position: 'absolute',
top: 'calc(50% - 50vh)',
left: '50%', left: '50%',
height: '100vh',
width: 1, width: 1,
pointerEvents: 'none', pointerEvents: 'none',
}} }}

View File

@ -15,6 +15,7 @@ import {
} from '#/state/preferences' } from '#/state/preferences'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import {useIsWithinMessage} from '#/components/dms/MessageContext'
import {useFullscreen} from '#/components/hooks/useFullscreen' import {useFullscreen} from '#/components/hooks/useFullscreen'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import { import {
@ -113,7 +114,8 @@ export function Controls({
}, [active, pause, setFocused]) }, [active, pause, setFocused])
// autoplay/pause based on visibility // autoplay/pause based on visibility
const autoplayDisabled = useAutoplayDisabled() const isWithinMessage = useIsWithinMessage()
const autoplayDisabled = useAutoplayDisabled() || isWithinMessage
useEffect(() => { useEffect(() => {
if (active) { if (active) {
if (onScreen) { if (onScreen) {