disable autoplay within messages and trim feelers (#5260)
parent
b04ecbe54d
commit
580b67ba37
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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',
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue