add gif embed player

This commit is contained in:
Hailey
2023-12-27 20:00:59 -08:00
parent 5f32d0d65a
commit 03d145e467
4 changed files with 152 additions and 14 deletions
+17 -10
View File
@@ -16,8 +16,7 @@ export type EmbedPlayerParams =
| {type: 'apple_music_album'; albumId: string; playerUri: string}
| {type: 'apple_music_song'; songId: string; playerUri: string}
| {type: 'vimeo_video'; videoId: string; playerUri: string}
| {type: 'giphy_gif'; playerUri: string}
| {type: 'tenor_gif'; playerUri: string}
| {type: 'gif'; playerUri: string}
export function parseEmbedPlayerFromUrl(
url: string,
@@ -187,7 +186,7 @@ export function parseEmbedPlayerFromUrl(
if (id) {
return {
type: 'giphy_gif',
type: 'gif',
playerUri: `https://i.giphy.com/media/${id}/giphy.gif`,
}
}
@@ -204,12 +203,12 @@ export function parseEmbedPlayerFromUrl(
if (media === 'media') {
if (idOrFilename === 'giphy.gif' || idOrFilename === 'giphy.gif') {
return {
type: 'giphy_gif',
type: 'gif',
playerUri: `https://i.giphy.com/media/${trackingOrId}/giphy.gif`,
}
} else if (filename === 'giphy.gif' || filename === 'giphy.gif') {
return {
type: 'giphy_gif',
type: 'gif',
playerUri: `https://i.giphy.com/media/${idOrFilename}/giphy.gif`,
}
}
@@ -223,14 +222,14 @@ export function parseEmbedPlayerFromUrl(
if (mediaOrFilename === 'media' && filename) {
return {
type: 'giphy_gif',
type: 'gif',
playerUri: `https://i.giphy.com/media/${
filename.split('.')[0]
}/giphy.gif`,
}
} else if (mediaOrFilename) {
return {
type: 'giphy_gif',
type: 'gif',
playerUri: `https://i.giphy.com/media/${
mediaOrFilename.split('.')[0]
}/giphy.gif`,
@@ -244,7 +243,7 @@ export function parseEmbedPlayerFromUrl(
if (parts.length === 4 && filename?.split('.').pop() === 'gif') {
return {
type: 'tenor_gif',
type: 'gif',
playerUri: url,
}
}
@@ -257,7 +256,7 @@ export function parseEmbedPlayerFromUrl(
const includesExt = filename.split('.').pop() === 'gif'
return {
type: 'tenor_gif',
type: 'gif',
playerUri: `${url}${!includesExt ? '.gif' : ''}`,
}
}
@@ -271,7 +270,7 @@ export function parseEmbedPlayerFromUrl(
if (ext === 'gif' || ext === 'webp') {
return {
type: 'tenor_gif',
type: 'gif',
playerUri: url,
}
}
@@ -314,3 +313,11 @@ export function getPlayerHeight({
return width
}
}
export function getGifHeight(
originalHeight: number,
originalWidth: number,
viewWidth: number,
) {
return (originalHeight / originalWidth) * viewWidth
}
@@ -0,0 +1,124 @@
import {EmbedPlayerParams, getGifHeight} from 'lib/strings/embed-player.ts'
import React from 'react'
import {Image, ImageLoadEventData} from 'expo-image'
import {
GestureResponderEvent,
LayoutChangeEvent,
Pressable,
StyleSheet,
View,
} from 'react-native'
import {isWeb} from 'platform/detection.ts'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
export function ExternalGifEmbed({
thumb,
params,
}: {
thumb?: string
params: EmbedPlayerParams
}) {
const isThumbLoaded = React.useRef(false)
const viewWidth = React.useRef(0)
const [isLoaded, setIsLoaded] = React.useState(false)
const [isAnimating, setIsAnimating] = React.useState(true)
const [height, setHeight] = React.useState(100)
const imageRef = React.useRef<Image>(null)
const onPlayPress = React.useCallback(
(event: GestureResponderEvent) => {
event.preventDefault()
if (!isLoaded) {
setIsLoaded(true)
return
}
if (isWeb) return
setIsAnimating(prev => {
if (prev) {
imageRef.current?.stopAnimating()
return false
} else {
imageRef.current?.startAnimating()
return true
}
})
},
[isLoaded],
)
const onLoad = React.useCallback((e: ImageLoadEventData) => {
if (isThumbLoaded.current) return
const scaledHeight = getGifHeight(
e.source.height,
e.source.width,
viewWidth.current,
)
setHeight(scaledHeight > 300 ? 300 : scaledHeight)
}, [])
const onLayout = React.useCallback((e: LayoutChangeEvent) => {
viewWidth.current = e.nativeEvent.layout.width
}, [])
return (
<Pressable
accessibilityRole="button"
style={[{height}, styles.topRadius, styles.imageContainer]}
onPress={onPlayPress}
onLayout={onLayout}>
{(!isLoaded || !isAnimating) && (
<View style={[styles.layer, styles.overlayLayer]}>
<Pressable
accessibilityRole="button"
accessibilityLabel="Play GIF"
accessibilityHint=""
onPress={onPlayPress}
style={[styles.overlayContainer, styles.topRadius]}>
<FontAwesomeIcon icon="play" size={42} color="white" />
</Pressable>
</View>
)}
<Image
source={{uri: !isLoaded ? thumb : params.playerUri}}
style={{flex: 1}}
ref={imageRef}
onLoad={onLoad}
contentFit="contain"
accessibilityIgnoresInvertColors
/>
</Pressable>
)
}
const styles = StyleSheet.create({
topRadius: {
borderTopLeftRadius: 6,
borderTopRightRadius: 6,
},
layer: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
overlayContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.5)',
},
imageContainer: {
width: '100%',
overflow: 'hidden',
},
overlayLayer: {
zIndex: 2,
},
})
@@ -8,6 +8,7 @@ import {AppBskyEmbedExternal} from '@atproto/api'
import {toNiceDomain} from 'lib/strings/url-helpers'
import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player'
import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed'
import {ExternalGifEmbed} from 'view/com/util/post-embeds/ExternalGifEmbed.tsx'
export const ExternalLinkEmbed = ({
link,
@@ -53,9 +54,12 @@ export const ExternalLinkEmbed = ({
/>
</View>
) : undefined}
{embedPlayerParams && (
{(embedPlayerParams && embedPlayerParams.type !== 'gif' && (
<ExternalPlayer link={link} params={embedPlayerParams} />
)}
)) ||
(embedPlayerParams && embedPlayerParams.type === 'gif' && (
<ExternalGifEmbed params={embedPlayerParams} thumb={link.thumb} />
))}
<View
style={{
paddingHorizontal: isMobile ? 10 : 14,
@@ -202,12 +202,11 @@ export function ExternalPlayer({
styles.topRadius,
]}
source={{
uri: 'https://media1.tenor.com/m/UNTqMDwqh1gAAAAC/hello-hi.gif',
uri: link.thumb,
}}
accessibilityIgnoresInvertColors
/>
)}
<PlaceholderOverlay
isLoading={isLoading}
isPlayerActive={isPlayerActive}
@@ -241,6 +240,10 @@ const styles = StyleSheet.create({
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.5)',
},
imageContainer: {
width: '100%',
overflow: 'hidden',
},
overlayLayer: {
zIndex: 2,
},