Skip to content

Commit

Permalink
[Videos] Video player - PR #2 - better web support (#4732)
Browse files Browse the repository at this point in the history
* attempt some sort of "usurping" system

* polling-based active video approach

* split into inner component again

* click to steal active video

* disable findAndActivateVideo on native

* new intersectionobserver approach - wip

* fix types

* disable perf optimisation to allow overflow

* make active player indicator subtler, clean up video utils

* partially fix double-playing

* start working on controls

* fullscreen API

* get buttons working somewhat

* rm source from where it shouldn't be

* use video elem as source of truth

* fix keyboard nav + mute state

* new icons, add fullscreen + time + fix play

* unmount when far offscreen + round 2dp

* listen globally to clicks rather than blur event

* move controls to new file

* reduce quality when not active

* add hover state to buttons

* stop propagation of videoplayer click

* move around autoplay effects

* increase background contrast

* add subtitles button

* add stopPropagation to root of video player

* clean up VideoWebControls

* fix chrome

* change quality based on focused state

* use autoLevelCapping instead of nextLevel

* get subtitle track from stream

* always use hlsjs

* rework hls into a ref

* render player earlier, allowing preload

* add error boundary

* clean up component structure and organisation

* rework fullscreen API

* disable fullscreen on iPhone

* don't play when ready on pause

* debounce buffering

* simplify giant list of event listeners

* update pref

* reduce prop drilling

* minimise rerenders in `ActiveViewContext`

* restore prop drilling

---------

Co-authored-by: Samuel Newman <[email protected]>
Co-authored-by: Hailey <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2024
1 parent b701e8c commit fff2c07
Show file tree
Hide file tree
Showing 32 changed files with 1,086 additions and 86 deletions.
1 change: 1 addition & 0 deletions assets/icons/arrowsDiagonalIn_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/arrowsDiagonalIn_stroke2_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/arrowsDiagonalOut_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/arrowsDiagonalOut_stroke2_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/cc_filled_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/cc_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pause_filled_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pause_filled_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pause_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pause_stroke2_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/play_filled_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/play_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/components/icons/ArrowsDiagonal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {createSinglePathSVG} from './TEMPLATE'

export const ArrowsDiagonalOut_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M14 5a1 1 0 1 1 0-2h6a1 1 0 0 1 1 1v6a1 1 0 1 1-2 0V6.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L17.586 5H14ZM4 13a1 1 0 0 1 1 1v3.586l4.293-4.293a1 1 0 0 1 1.414 1.414L6.414 19H10a1 1 0 1 1 0 2H4a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1Z',
})

export const ArrowsDiagonalIn_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M20.957 3.043a1 1 0 0 1 0 1.414L16.414 9H20a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1V4a1 1 0 1 1 2 0v3.586l4.543-4.543a1 1 0 0 1 1.414 0ZM3 14a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 1 1-2 0v-3.586l-4.543 4.543a1 1 0 0 1-1.414-1.414L7.586 15H4a1 1 0 0 1-1-1Z',
})

export const ArrowsDiagonalOut_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M13 4a1 1 0 0 1 1-1h5a2 2 0 0 1 2 2v5a1 1 0 1 1-2 0V6.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L17.586 5H14a1 1 0 0 1-1-1Zm-9 9a1 1 0 0 1 1 1v3.586l4.293-4.293a1 1 0 0 1 1.414 1.414L6.414 19H10a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2v-5a1 1 0 0 1 1-1Z',
})

export const ArrowsDiagonalIn_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M20.957 3.043a1 1 0 0 1 0 1.414L16.414 9H20a1 1 0 1 1 0 2h-5a2 2 0 0 1-2-2V4a1 1 0 1 1 2 0v3.586l4.543-4.543a1 1 0 0 1 1.414 0ZM3 14a1 1 0 0 1 1-1h5a2 2 0 0 1 2 2v5a1 1 0 1 1-2 0v-3.586l-4.543 4.543a1 1 0 0 1-1.414-1.414L7.586 15H4a1 1 0 0 1-1-1Z',
})
9 changes: 9 additions & 0 deletions src/components/icons/CC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {createSinglePathSVG} from './TEMPLATE'

export const CC_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4Zm2 1v14h14V5H5Zm10.957 6.293a1 1 0 1 0 0 1.414 1 1 0 0 1 1.414 1.414 3 3 0 1 1 0-4.242 1 1 0 0 1-1.414 1.414Zm-6.331-.22a1 1 0 1 0 .331 1.634 1 1 0 0 1 1.414 1.414 3 3 0 1 1 0-4.242 1 1 0 0 1-1.414 1.414.994.994 0 0 0-.331-.22Z',
})

export const CC_Filled_Corner0_Rounded = createSinglePathSVG({
path: 'M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4Zm11.543 7.293a1 1 0 0 1 1.414 0 1 1 0 0 0 1.414-1.414 3 3 0 1 0 0 4.242 1 1 0 0 0-1.414-1.414 1 1 0 0 1-1.414-1.414Zm-6 0a1 1 0 0 1 1.414 0 1 1 0 0 0 1.414-1.414 3 3 0 1 0 0 4.243 1 1 0 0 0-1.414-1.415 1 1 0 0 1-1.414-1.414Z',
})
17 changes: 17 additions & 0 deletions src/components/icons/Pause.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {createSinglePathSVG} from './TEMPLATE'

export const Pause_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M4 4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4Zm2 1v14h2V5H6Zm8-1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1V4Zm2 1v14h2V5h-2Z',
})

export const Pause_Filled_Corner0_Rounded = createSinglePathSVG({
path: 'M4 4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4ZM14 4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1V4Z',
})

export const Pause_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M4 6a3 3 0 0 1 6 0v12a3 3 0 1 1-6 0V6Zm3-1a1 1 0 0 0-1 1v12a1 1 0 1 0 2 0V6a1 1 0 0 0-1-1Zm7 1a3 3 0 1 1 6 0v12a3 3 0 1 1-6 0V6Zm3-1a1 1 0 0 0-1 1v12a1 1 0 1 0 2 0V6a1 1 0 0 0-1-1Z',
})

export const Pause_Filled_Corner2_Rounded = createSinglePathSVG({
path: 'M4 6a3 3 0 0 1 6 0v12a3 3 0 1 1-6 0V6ZM14 6a3 3 0 1 1 6 0v12a3 3 0 1 1-6 0V6Z',
})
8 changes: 8 additions & 0 deletions src/components/icons/Play.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import {createSinglePathSVG} from './TEMPLATE'

export const Play_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M5.507 2.13a1 1 0 0 1 1.008.013l15 9a1 1 0 0 1 0 1.714l-15 9A1 1 0 0 1 5 21V3a1 1 0 0 1 .507-.87ZM7 4.766v14.468L19.056 12 7 4.766Z',
})

export const Play_Filled_Corner0_Rounded = createSinglePathSVG({
path: 'M6.514 2.143A1 1 0 0 0 5 3v18a1 1 0 0 0 1.514.858l15-9a1 1 0 0 0 0-1.716l-15-9Z',
})

export const Play_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M5 5.086C5 2.736 7.578 1.3 9.576 2.534L20.77 9.448c1.899 1.172 1.899 3.932 0 5.104L9.576 21.466C7.578 22.701 5 21.263 5 18.914V5.086Zm3.525-.85A1 1 0 0 0 7 5.085v13.828a1 1 0 0 0 1.525.85l11.194-6.913a1 1 0 0 0 0-1.702L8.525 4.235Z',
})
Expand Down
1 change: 1 addition & 0 deletions src/platform/detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const isMobileWeb =
isWeb &&
// @ts-ignore we know window exists -prf
global.window.matchMedia(isMobileWebMediaQuery)?.matches
export const isIPhoneWeb = isWeb && /iPhone/.test(navigator.userAgent)

export const deviceLocales = dedupArray(
getLocales?.()
Expand Down
3 changes: 0 additions & 3 deletions src/screens/Messages/Conversation/MessagesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,6 @@ export function MessagesList({
renderItem={renderItem}
keyExtractor={keyExtractor}
disableFullWindowScroll={true}
// Prevents wrong position in Firefox when sending a message
// as well as scroll getting stuck on Chome when scrolling upwards.
disableContainStyle={true}
disableVirtualization={true}
style={animatedListStyle}
// The extra two items account for the header and the footer components
Expand Down
2 changes: 2 additions & 0 deletions src/state/persisted/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const schema = z.object({
disableAutoplay: z.boolean().optional(),
kawaii: z.boolean().optional(),
hasCheckedForStarterPack: z.boolean().optional(),
subtitlesEnabled: z.boolean().optional(),
/** @deprecated */
mutedThreads: z.array(z.string()),
})
Expand Down Expand Up @@ -133,6 +134,7 @@ export const defaults: Schema = {
disableAutoplay: PlatformInfo.getIsReducedMotionEnabled(),
kawaii: false,
hasCheckedForStarterPack: false,
subtitlesEnabled: true,
}

export function tryParse(rawData: string): Schema | undefined {
Expand Down
6 changes: 5 additions & 1 deletion src/state/preferences/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Provider as InAppBrowserProvider} from './in-app-browser'
import {Provider as KawaiiProvider} from './kawaii'
import {Provider as LanguagesProvider} from './languages'
import {Provider as LargeAltBadgeProvider} from './large-alt-badge'
import {Provider as SubtitlesProvider} from './subtitles'
import {Provider as UsedStarterPacksProvider} from './used-starter-packs'

export {
Expand All @@ -24,6 +25,7 @@ export {
export * from './hidden-posts'
export {useLabelDefinitions} from './label-defs'
export {useLanguagePrefs, useLanguagePrefsApi} from './languages'
export {useSetSubtitlesEnabled, useSubtitlesEnabled} from './subtitles'

export function Provider({children}: React.PropsWithChildren<{}>) {
return (
Expand All @@ -36,7 +38,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
<DisableHapticsProvider>
<AutoplayProvider>
<UsedStarterPacksProvider>
<KawaiiProvider>{children}</KawaiiProvider>
<SubtitlesProvider>
<KawaiiProvider>{children}</KawaiiProvider>
</SubtitlesProvider>
</UsedStarterPacksProvider>
</AutoplayProvider>
</DisableHapticsProvider>
Expand Down
42 changes: 42 additions & 0 deletions src/state/preferences/subtitles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'

import * as persisted from '#/state/persisted'

type StateContext = boolean
type SetContext = (v: boolean) => void

const stateContext = React.createContext<StateContext>(
Boolean(persisted.defaults.subtitlesEnabled),
)
const setContext = React.createContext<SetContext>((_: boolean) => {})

export function Provider({children}: {children: React.ReactNode}) {
const [state, setState] = React.useState(
Boolean(persisted.get('subtitlesEnabled')),
)

const setStateWrapped = React.useCallback(
(subtitlesEnabled: persisted.Schema['subtitlesEnabled']) => {
setState(Boolean(subtitlesEnabled))
persisted.write('subtitlesEnabled', subtitlesEnabled)
},
[setState],
)

React.useEffect(() => {
return persisted.onUpdate('subtitlesEnabled', nextSubtitlesEnabled => {
setState(Boolean(nextSubtitlesEnabled))
})
}, [setStateWrapped])

return (
<stateContext.Provider value={state}>
<setContext.Provider value={setStateWrapped}>
{children}
</setContext.Provider>
</stateContext.Provider>
)
}

export const useSubtitlesEnabled = () => React.useContext(stateContext)
export const useSetSubtitlesEnabled = () => React.useContext(setContext)
1 change: 0 additions & 1 deletion src/view/com/posts/FeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,6 @@ const styles = StyleSheet.create({
paddingRight: 15,
// @ts-ignore web only -prf
cursor: 'pointer',
overflow: 'hidden',
},
replyLine: {
width: 2,
Expand Down
2 changes: 0 additions & 2 deletions src/view/com/util/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ export type ListProps<ItemT> = Omit<
// Web only prop to contain the scroll to the container rather than the window
disableFullWindowScroll?: boolean
sideBorders?: boolean
// Web only prop to disable a perf optimization (which would otherwise be on).
disableContainStyle?: boolean
}
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>

Expand Down
22 changes: 4 additions & 18 deletions src/view/com/util/List.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/rean

import {batchedUpdates} from '#/lib/batchedUpdates'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useScrollHandlers} from '#/lib/ScrollContext'
import {isSafari} from 'lib/browser'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {addStyle} from 'lib/styles'
import {addStyle} from '#/lib/styles'

export type ListMethods = any // TODO: Better types.
export type ListProps<ItemT> = Omit<
Expand All @@ -26,8 +25,6 @@ export type ListProps<ItemT> = Omit<
// Web only prop to contain the scroll to the container rather than the window
disableFullWindowScroll?: boolean
sideBorders?: boolean
// Web only prop to disable a perf optimization (which would otherwise be on).
disableContainStyle?: boolean
}
export type ListRef = React.MutableRefObject<any | null> // TODO: Better types.

Expand Down Expand Up @@ -60,7 +57,6 @@ function ListImpl<ItemT>(
extraData,
style,
sideBorders = true,
disableContainStyle,
...props
}: ListProps<ItemT>,
ref: React.Ref<ListMethods>,
Expand Down Expand Up @@ -364,7 +360,6 @@ function ListImpl<ItemT>(
renderItem={renderItem}
extraData={extraData}
onItemSeen={onItemSeen}
disableContainStyle={disableContainStyle}
/>
)
})}
Expand Down Expand Up @@ -442,7 +437,6 @@ let Row = function RowImpl<ItemT>({
renderItem,
extraData: _unused,
onItemSeen,
disableContainStyle,
}: {
item: ItemT
index: number
Expand All @@ -452,7 +446,6 @@ let Row = function RowImpl<ItemT>({
| ((data: {index: number; item: any; separators: any}) => React.ReactNode)
extraData: any
onItemSeen: ((item: any) => void) | undefined
disableContainStyle?: boolean
}): React.ReactNode {
const rowRef = React.useRef(null)
const intersectionTimeout = React.useRef<NodeJS.Timer | undefined>(undefined)
Expand Down Expand Up @@ -501,11 +494,8 @@ let Row = function RowImpl<ItemT>({
return null
}

const shouldDisableContainStyle = disableContainStyle || isSafari
return (
<View
style={shouldDisableContainStyle ? undefined : styles.contain}
ref={rowRef}>
<View ref={rowRef}>
{renderItem({item, index, separators: null as any})}
</View>
)
Expand Down Expand Up @@ -576,10 +566,6 @@ const styles = StyleSheet.create({
marginLeft: 'auto',
marginRight: 'auto',
},
contain: {
// @ts-ignore web only
contain: 'layout paint',
},
minHeightViewport: {
// @ts-ignore web only
minHeight: '100vh',
Expand Down
Loading

0 comments on commit fff2c07

Please sign in to comment.