mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
3import {Image} from 'expo-image'
4import {AppBskyFeedDefs} from '@atproto/api'
5import {Trans} from '@lingui/macro'
6
7import {isTenorGifUri} from '#/lib/strings/embed-player'
8import {atoms as a, useTheme} from '#/alf'
9import {MediaInsetBorder} from '#/components/MediaInsetBorder'
10import {Text} from '#/components/Typography'
11import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
12import * as bsky from '#/types/bsky'
13
14/**
15 * Streamlined MediaPreview component which just handles images, gifs, and videos
16 */
17export function Embed({
18 embed,
19 style,
20}: {
21 embed: AppBskyFeedDefs.PostView['embed']
22 style?: StyleProp<ViewStyle>
23}) {
24 const e = bsky.post.parseEmbed(embed)
25
26 if (!e) return null
27
28 if (e.type === 'images') {
29 return (
30 <Outer style={style}>
31 {e.view.images.map(image => (
32 <ImageItem
33 key={image.thumb}
34 thumbnail={image.thumb}
35 alt={image.alt}
36 />
37 ))}
38 </Outer>
39 )
40 } else if (e.type === 'link') {
41 if (!e.view.external.thumb) return null
42 if (!isTenorGifUri(e.view.external.uri)) return null
43 return (
44 <Outer style={style}>
45 <GifItem
46 thumbnail={e.view.external.thumb}
47 alt={e.view.external.title}
48 />
49 </Outer>
50 )
51 } else if (e.type === 'video') {
52 return (
53 <Outer style={style}>
54 <VideoItem thumbnail={e.view.thumbnail} alt={e.view.alt} />
55 </Outer>
56 )
57 }
58
59 return null
60}
61
62export function Outer({
63 children,
64 style,
65}: {
66 children?: React.ReactNode
67 style?: StyleProp<ViewStyle>
68}) {
69 return <View style={[a.flex_row, a.gap_xs, style]}>{children}</View>
70}
71
72export function ImageItem({
73 thumbnail,
74 alt,
75 children,
76}: {
77 thumbnail: string
78 alt?: string
79 children?: React.ReactNode
80}) {
81 const t = useTheme()
82 return (
83 <View style={[a.relative, a.flex_1, {aspectRatio: 1, maxWidth: 100}]}>
84 <Image
85 key={thumbnail}
86 source={{uri: thumbnail}}
87 style={[a.flex_1, a.rounded_xs, t.atoms.bg_contrast_25]}
88 contentFit="cover"
89 accessible={true}
90 accessibilityIgnoresInvertColors
91 accessibilityHint={alt}
92 accessibilityLabel=""
93 />
94 <MediaInsetBorder style={[a.rounded_xs]} />
95 {children}
96 </View>
97 )
98}
99
100export function GifItem({thumbnail, alt}: {thumbnail: string; alt?: string}) {
101 return (
102 <ImageItem thumbnail={thumbnail} alt={alt}>
103 <View style={[a.absolute, a.inset_0, a.justify_center, a.align_center]}>
104 <PlayButtonIcon size={24} />
105 </View>
106 <View style={styles.altContainer}>
107 <Text style={styles.alt}>
108 <Trans>GIF</Trans>
109 </Text>
110 </View>
111 </ImageItem>
112 )
113}
114
115export function VideoItem({
116 thumbnail,
117 alt,
118}: {
119 thumbnail?: string
120 alt?: string
121}) {
122 if (!thumbnail) {
123 return (
124 <View
125 style={[
126 {backgroundColor: 'black'},
127 a.flex_1,
128 {aspectRatio: 1, maxWidth: 100},
129 a.justify_center,
130 a.align_center,
131 ]}>
132 <PlayButtonIcon size={24} />
133 </View>
134 )
135 }
136 return (
137 <ImageItem thumbnail={thumbnail} alt={alt}>
138 <View style={[a.absolute, a.inset_0, a.justify_center, a.align_center]}>
139 <PlayButtonIcon size={24} />
140 </View>
141 </ImageItem>
142 )
143}
144
145const styles = StyleSheet.create({
146 altContainer: {
147 backgroundColor: 'rgba(0, 0, 0, 0.75)',
148 borderRadius: 6,
149 paddingHorizontal: 6,
150 paddingVertical: 3,
151 position: 'absolute',
152 right: 5,
153 bottom: 5,
154 zIndex: 2,
155 },
156 alt: {
157 color: 'white',
158 fontSize: 7,
159 fontWeight: '600',
160 },
161})