Files
bsky-social-app/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
T
Eric Bailey 45f0f7eefe Port post embeds to new arch (#7408)
* Direct port of embeds to new arch

(cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6)

* Re-org

* Split out ListEmbed and FeedEmbed

* Split out ImageEmbed

* DRY up a bit

* Port over ExternalLinkEmbed

* Port over Player and Gif embeds

* Migrate ComposerReplyTo

* Replace other usages of old post-embeds

* Migrate view contexts

* Copy pasta VideoEmbed

* Copy pasta GifEmbed

* Swap in new file location

* Clean up

* Fix up native

* Add back in correct moderation on List and Feed embeds

* Format

* Prettier

* delete old video utils

* move bandwidth-estimate.ts

* Remove log

* Add LazyQuoteEmbed for composer use

* Clean up unused things

* Remove remaining items

* Prettier

* Fix imports

* Handle nested quotes same as prod

* Add back silenced error handling

* Fix lint

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2025-06-13 12:05:41 -05:00

115 lines
3.0 KiB
TypeScript

import React, {
useCallback,
useEffect,
useId,
useMemo,
useRef,
useState,
} from 'react'
import {useWindowDimensions} from 'react-native'
import {isNative, isWeb} from '#/platform/detection'
const Context = React.createContext<{
activeViewId: string | null
setActiveView: (viewId: string) => void
sendViewPosition: (viewId: string, y: number) => void
} | null>(null)
export function Provider({children}: {children: React.ReactNode}) {
if (!isWeb) {
throw new Error('ActiveVideoWebContext may only be used on web.')
}
const [activeViewId, setActiveViewId] = useState<string | null>(null)
const activeViewLocationRef = useRef(Infinity)
const {height: windowHeight} = useWindowDimensions()
// minimising re-renders by using refs
const manuallySetRef = useRef(false)
const activeViewIdRef = useRef(activeViewId)
useEffect(() => {
activeViewIdRef.current = activeViewId
}, [activeViewId])
const setActiveView = useCallback(
(viewId: string) => {
setActiveViewId(viewId)
manuallySetRef.current = true
// we don't know the exact position, but it's definitely on screen
// so just guess that it's in the middle. Any value is fine
// so long as it's not offscreen
activeViewLocationRef.current = windowHeight / 2
},
[windowHeight],
)
const sendViewPosition = useCallback(
(viewId: string, y: number) => {
if (isNative) return
if (viewId === activeViewIdRef.current) {
activeViewLocationRef.current = y
} else {
if (
distanceToIdealPosition(y) <
distanceToIdealPosition(activeViewLocationRef.current)
) {
// if the old view was manually set, only usurp if the old view is offscreen
if (
manuallySetRef.current &&
withinViewport(activeViewLocationRef.current)
) {
return
}
setActiveViewId(viewId)
activeViewLocationRef.current = y
manuallySetRef.current = false
}
}
function distanceToIdealPosition(yPos: number) {
return Math.abs(yPos - windowHeight / 2.5)
}
function withinViewport(yPos: number) {
return yPos > 0 && yPos < windowHeight
}
},
[windowHeight],
)
const value = useMemo(
() => ({
activeViewId,
setActiveView,
sendViewPosition,
}),
[activeViewId, setActiveView, sendViewPosition],
)
return <Context.Provider value={value}>{children}</Context.Provider>
}
export function useActiveVideoWeb() {
const context = React.useContext(Context)
if (!context) {
throw new Error(
'useActiveVideoWeb must be used within a ActiveVideoWebProvider',
)
}
const {activeViewId, setActiveView, sendViewPosition} = context
const id = useId()
return {
active: activeViewId === id,
setActive: () => {
setActiveView(id)
},
currentActiveView: activeViewId,
sendPosition: (y: number) => sendViewPosition(id, y),
}
}