Skip to content

Commit

Permalink
Bring video cropping in line with images (#7462)
Browse files Browse the repository at this point in the history
* Mimic image cropping for videos on web

* Same on native
  • Loading branch information
estrattonbailey authored Jan 18, 2025
1 parent 6f3f116 commit 3e0ac0a
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 55 deletions.
67 changes: 47 additions & 20 deletions src/view/com/util/post-embeds/VideoEmbed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {AppBskyEmbedVideo} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'

import {clamp} from '#/lib/numbers'
import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage'
import {VideoEmbedInnerNative} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative'
import {atoms as a} from '#/alf'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {useThrottledValue} from '#/components/hooks/useThrottledValue'
import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
Expand All @@ -16,9 +16,11 @@ import * as VideoFallback from './VideoEmbedInner/VideoFallback'

interface Props {
embed: AppBskyEmbedVideo.View
crop?: 'none' | 'square' | 'constrained'
}

export function VideoEmbed({embed}: Props) {
export function VideoEmbed({embed, crop}: Props) {
const t = useTheme()
const [key, setKey] = useState(0)

const renderError = useCallback(
Expand All @@ -28,26 +30,51 @@ export function VideoEmbed({embed}: Props) {
[key],
)

let aspectRatio = 16 / 9
if (embed.aspectRatio) {
const {width, height} = embed.aspectRatio
aspectRatio = width / height
aspectRatio = clamp(aspectRatio, 1 / 1, 3 / 1)
let aspectRatio: number | undefined
const dims = embed.aspectRatio
if (dims) {
aspectRatio = dims.width / dims.height
if (Number.isNaN(aspectRatio)) {
aspectRatio = undefined
}
}

let constrained: number | undefined
let max: number | undefined
if (aspectRatio !== undefined) {
const ratio = 1 / 2 // max of 1:2 ratio in feeds
constrained = Math.max(aspectRatio, ratio)
max = Math.max(aspectRatio, 0.25) // max of 1:4 in thread
}
const cropDisabled = crop === 'none'

const contents = (
<ErrorBoundary renderError={renderError} key={key}>
<InnerWrapper embed={embed} />
</ErrorBoundary>
)

return (
<View
style={[
a.w_full,
a.rounded_md,
a.overflow_hidden,
{aspectRatio},
{backgroundColor: 'black'},
a.mt_xs,
]}>
<ErrorBoundary renderError={renderError} key={key}>
<InnerWrapper embed={embed} />
</ErrorBoundary>
<View style={[a.pt_xs]}>
{cropDisabled ? (
<View
style={[
a.w_full,
a.overflow_hidden,
{aspectRatio: max ?? 1},
a.rounded_md,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
{contents}
</View>
) : (
<ConstrainedImage
fullBleed={crop === 'square'}
aspectRatio={constrained || 1}>
{contents}
</ConstrainedImage>
)}
</View>
)
}
Expand Down
90 changes: 56 additions & 34 deletions src/view/com/util/post-embeds/VideoEmbed.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'

import {isFirefox} from '#/lib/browser'
import {clamp} from '#/lib/numbers'
import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage'
import {
HLSUnsupportedError,
VideoEmbedInnerWeb,
Expand All @@ -18,7 +18,13 @@ import {ErrorBoundary} from '../ErrorBoundary'
import {useActiveVideoWeb} from './ActiveVideoWebContext'
import * as VideoFallback from './VideoEmbedInner/VideoFallback'

export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) {
export function VideoEmbed({
embed,
crop,
}: {
embed: AppBskyEmbedVideo.View
crop?: 'none' | 'square' | 'constrained'
}) {
const ref = useRef<HTMLDivElement>(null)
const {active, setActive, sendPosition, currentActiveView} =
useActiveVideoWeb()
Expand Down Expand Up @@ -52,42 +58,58 @@ export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) {
[key],
)

let aspectRatio = 16 / 9
let aspectRatio: number | undefined
const dims = embed.aspectRatio
if (dims) {
aspectRatio = dims.width / dims.height
if (Number.isNaN(aspectRatio)) {
aspectRatio = undefined
}
}

if (embed.aspectRatio) {
const {width, height} = embed.aspectRatio
// min: 3/1, max: square
aspectRatio = clamp(width / height, 1 / 1, 3 / 1)
let constrained: number | undefined
let max: number | undefined
if (aspectRatio !== undefined) {
const ratio = 1 / 2 // max of 1:2 ratio in feeds
constrained = Math.max(aspectRatio, ratio)
max = Math.max(aspectRatio, 0.25) // max of 1:4 in thread
}
const cropDisabled = crop === 'none'

const contents = (
<div
ref={ref}
style={{display: 'flex', flex: 1, cursor: 'default'}}
onClick={evt => evt.stopPropagation()}>
<ErrorBoundary renderError={renderError} key={key}>
<ViewportObserver
sendPosition={sendPosition}
isAnyViewActive={currentActiveView !== null}>
<VideoEmbedInnerWeb
embed={embed}
active={active}
setActive={setActive}
onScreen={onScreen}
lastKnownTime={lastKnownTime}
/>
</ViewportObserver>
</ErrorBoundary>
</div>
)

return (
<View
style={[
a.w_full,
{aspectRatio},
{backgroundColor: 'black'},
a.relative,
a.rounded_md,
a.mt_xs,
]}>
<div
ref={ref}
style={{display: 'flex', flex: 1, cursor: 'default'}}
onClick={evt => evt.stopPropagation()}>
<ErrorBoundary renderError={renderError} key={key}>
<ViewportObserver
sendPosition={sendPosition}
isAnyViewActive={currentActiveView !== null}>
<VideoEmbedInnerWeb
embed={embed}
active={active}
setActive={setActive}
onScreen={onScreen}
lastKnownTime={lastKnownTime}
/>
</ViewportObserver>
</ErrorBoundary>
</div>
<View style={[a.pt_xs]}>
{cropDisabled ? (
<View style={[a.w_full, a.overflow_hidden, {aspectRatio: max ?? 1}]}>
{contents}
</View>
) : (
<ConstrainedImage
fullBleed={crop === 'square'}
aspectRatio={constrained || 1}>
{contents}
</ConstrainedImage>
)}
</View>
)
}
Expand Down
11 changes: 10 additions & 1 deletion src/view/com/util/post-embeds/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,16 @@ export function PostEmbeds({
if (AppBskyEmbedVideo.isView(embed)) {
return (
<ContentHider modui={moderation?.ui('contentMedia')}>
<VideoEmbed embed={embed} />
<VideoEmbed
embed={embed}
crop={
viewContext === PostEmbedViewContext.ThreadHighlighted
? 'none'
: viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
? 'square'
: 'constrained'
}
/>
</ContentHider>
)
}
Expand Down

0 comments on commit 3e0ac0a

Please sign in to comment.