diff options
Diffstat (limited to 'src/view/com/util')
-rw-r--r-- | src/view/com/util/Html.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/List.tsx | 45 | ||||
-rw-r--r-- | src/view/com/util/PostMeta.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/forms/NativeDropdown.web.tsx | 241 | ||||
-rw-r--r-- | src/view/com/util/forms/PostDropdownBtn.tsx | 29 | ||||
-rw-r--r-- | src/view/com/util/post-ctrls/PostCtrls.tsx | 3 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/ExternalLinkEmbed.tsx | 18 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx | 251 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/YoutubeEmbed.tsx | 55 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/index.tsx | 18 |
10 files changed, 591 insertions, 77 deletions
diff --git a/src/view/com/util/Html.tsx b/src/view/com/util/Html.tsx index 1590955a2..2e4719481 100644 --- a/src/view/com/util/Html.tsx +++ b/src/view/com/util/Html.tsx @@ -30,6 +30,7 @@ export function H1({children}: React.PropsWithChildren<{}>) { const styles = useStyles() const pal = usePalette('default') const typography = useTheme().typography['title-xl'] + // @ts-ignore Expo's TextStyle definition seems to have gotten away from RN's -prf return <ExpoH1 style={[typography, pal.text, styles.h1]}>{children}</ExpoH1> } @@ -37,6 +38,7 @@ export function H2({children}: React.PropsWithChildren<{}>) { const styles = useStyles() const pal = usePalette('default') const typography = useTheme().typography['title-lg'] + // @ts-ignore Expo's TextStyle definition seems to have gotten away from RN's -prf return <ExpoH2 style={[typography, pal.text, styles.h2]}>{children}</ExpoH2> } @@ -44,6 +46,7 @@ export function H3({children}: React.PropsWithChildren<{}>) { const styles = useStyles() const pal = usePalette('default') const typography = useTheme().typography.title + // @ts-ignore Expo's TextStyle definition seems to have gotten away from RN's -prf return <ExpoH3 style={[typography, pal.text, styles.h3]}>{children}</ExpoH3> } @@ -51,6 +54,7 @@ export function H4({children}: React.PropsWithChildren<{}>) { const styles = useStyles() const pal = usePalette('default') const typography = useTheme().typography['title-sm'] + // @ts-ignore Expo's TextStyle definition seems to have gotten away from RN's -prf return <ExpoH4 style={[typography, pal.text, styles.h4]}>{children}</ExpoH4> } diff --git a/src/view/com/util/List.tsx b/src/view/com/util/List.tsx index 2acc3f4b3..9abd7d35a 100644 --- a/src/view/com/util/List.tsx +++ b/src/view/com/util/List.tsx @@ -1,27 +1,42 @@ import React, {memo, startTransition} from 'react' -import {FlatListProps} from 'react-native' +import {FlatListProps, RefreshControl} from 'react-native' import {FlatList_INTERNAL} from './Views' +import {addStyle} from 'lib/styles' import {useScrollHandlers} from '#/lib/ScrollContext' import {runOnJS, useSharedValue} from 'react-native-reanimated' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' +import {usePalette} from '#/lib/hooks/usePalette' export type ListMethods = FlatList_INTERNAL export type ListProps<ItemT> = Omit< FlatListProps<ItemT>, - 'onScroll' // Use ScrollContext instead. + | 'onScroll' // Use ScrollContext instead. + | 'refreshControl' // Pass refreshing and/or onRefresh instead. + | 'contentOffset' // Pass headerOffset instead. > & { onScrolledDownChange?: (isScrolledDown: boolean) => void + headerOffset?: number + refreshing?: boolean + onRefresh?: () => void } export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null> const SCROLLED_DOWN_LIMIT = 200 function ListImpl<ItemT>( - {onScrolledDownChange, ...props}: ListProps<ItemT>, + { + onScrolledDownChange, + refreshing, + onRefresh, + headerOffset, + style, + ...props + }: ListProps<ItemT>, ref: React.Ref<ListMethods>, ) { const isScrolledDown = useSharedValue(false) const contextScrollHandlers = useScrollHandlers() + const pal = usePalette('default') function handleScrolledDownChange(didScrollDown: boolean) { startTransition(() => { @@ -49,12 +64,36 @@ function ListImpl<ItemT>( }, }) + let refreshControl + if (refreshing !== undefined || onRefresh !== undefined) { + refreshControl = ( + <RefreshControl + refreshing={refreshing ?? false} + onRefresh={onRefresh} + tintColor={pal.colors.text} + titleColor={pal.colors.text} + progressViewOffset={headerOffset} + /> + ) + } + + let contentOffset + if (headerOffset != null) { + style = addStyle(style, { + paddingTop: headerOffset, + }) + contentOffset = {x: 0, y: headerOffset * -1} + } + return ( <FlatList_INTERNAL {...props} scrollIndicatorInsets={{right: 1}} + contentOffset={contentOffset} + refreshControl={refreshControl} onScroll={scrollHandler} scrollEventThrottle={1} + style={style} ref={ref} /> ) diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index eef7094cd..b9c3842b3 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -11,6 +11,7 @@ import {sanitizeHandle} from 'lib/strings/handles' import {isAndroid} from 'platform/detection' import {TimeElapsed} from './TimeElapsed' import {makeProfileLink} from 'lib/routes/links' +import {ModerationUI} from '@atproto/api' interface PostMetaOpts { author: { @@ -23,6 +24,7 @@ interface PostMetaOpts { postHref: string timestamp: string showAvatar?: boolean + avatarModeration?: ModerationUI avatarSize?: number displayNameType?: TypographyVariant displayNameStyle?: StyleProp<TextStyle> @@ -41,7 +43,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { <UserAvatar avatar={opts.author.avatar} size={opts.avatarSize || 16} - // TODO moderation + moderation={opts.avatarModeration} /> </View> )} diff --git a/src/view/com/util/forms/NativeDropdown.web.tsx b/src/view/com/util/forms/NativeDropdown.web.tsx new file mode 100644 index 000000000..9e9888ad8 --- /dev/null +++ b/src/view/com/util/forms/NativeDropdown.web.tsx @@ -0,0 +1,241 @@ +import React from 'react' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import * as DropdownMenu from '@radix-ui/react-dropdown-menu' +import {Pressable, StyleSheet, View, Text} from 'react-native' +import {IconProp} from '@fortawesome/fontawesome-svg-core' +import {MenuItemCommonProps} from 'zeego/lib/typescript/menu' +import {usePalette} from 'lib/hooks/usePalette' +import {useTheme} from 'lib/ThemeContext' +import {HITSLOP_10} from 'lib/constants' + +// Custom Dropdown Menu Components +// == +export const DropdownMenuRoot = DropdownMenu.Root +export const DropdownMenuContent = DropdownMenu.Content + +type ItemProps = React.ComponentProps<(typeof DropdownMenu)['Item']> +export const DropdownMenuItem = (props: ItemProps & {testID?: string}) => { + const theme = useTheme() + const [focused, setFocused] = React.useState(false) + const backgroundColor = theme.colorScheme === 'dark' ? '#fff1' : '#0001' + + return ( + <DropdownMenu.Item + {...props} + style={StyleSheet.flatten([ + styles.item, + focused && {backgroundColor: backgroundColor}, + ])} + onFocus={() => { + setFocused(true) + }} + onBlur={() => { + setFocused(false) + }} + /> + ) +} + +// Types for Dropdown Menu and Items +export type DropdownItem = { + label: string | 'separator' + onPress?: () => void + testID?: string + icon?: { + ios: MenuItemCommonProps['ios'] + android: string + web: IconProp + } +} +type Props = { + items: DropdownItem[] + testID?: string + accessibilityLabel?: string + accessibilityHint?: string +} + +export function NativeDropdown({ + items, + children, + testID, + accessibilityLabel, + accessibilityHint, +}: React.PropsWithChildren<Props>) { + const pal = usePalette('default') + const theme = useTheme() + const dropDownBackgroundColor = + theme.colorScheme === 'dark' ? pal.btn : pal.view + const [open, setOpen] = React.useState(false) + const buttonRef = React.useRef<HTMLButtonElement>(null) + const menuRef = React.useRef<HTMLDivElement>(null) + const {borderColor: separatorColor} = + theme.colorScheme === 'dark' ? pal.borderDark : pal.border + + React.useEffect(() => { + function clickHandler(e: MouseEvent) { + const t = e.target + + if (!open) return + if (!t) return + if (!buttonRef.current || !menuRef.current) return + + if ( + t !== buttonRef.current && + !buttonRef.current.contains(t as Node) && + t !== menuRef.current && + !menuRef.current.contains(t as Node) + ) { + // prevent clicking through to links beneath dropdown + // only applies to mobile web + e.preventDefault() + e.stopPropagation() + + // close menu + setOpen(false) + } + } + + function keydownHandler(e: KeyboardEvent) { + if (e.key === 'Escape' && open) { + setOpen(false) + } + } + + document.addEventListener('click', clickHandler, true) + window.addEventListener('keydown', keydownHandler, true) + return () => { + document.removeEventListener('click', clickHandler, true) + window.removeEventListener('keydown', keydownHandler, true) + } + }, [open, setOpen]) + + return ( + <DropdownMenuRoot open={open} onOpenChange={o => setOpen(o)}> + <DropdownMenu.Trigger asChild onPointerDown={e => e.preventDefault()}> + <Pressable + ref={buttonRef as unknown as React.Ref<View>} + testID={testID} + accessibilityRole="button" + accessibilityLabel={accessibilityLabel} + accessibilityHint={accessibilityHint} + onPress={() => setOpen(o => !o)} + hitSlop={HITSLOP_10}> + {children} + </Pressable> + </DropdownMenu.Trigger> + + <DropdownMenu.Portal> + <DropdownMenu.Content + ref={menuRef} + style={ + StyleSheet.flatten([ + styles.content, + dropDownBackgroundColor, + ]) as React.CSSProperties + } + loop> + {items.map((item, index) => { + if (item.label === 'separator') { + return ( + <DropdownMenu.Separator + key={getKey(item.label, index, item.testID)} + style={ + StyleSheet.flatten([ + styles.separator, + {backgroundColor: separatorColor}, + ]) as React.CSSProperties + } + /> + ) + } + if (index > 1 && items[index - 1].label === 'separator') { + return ( + <DropdownMenu.Group + key={getKey(item.label, index, item.testID)}> + <DropdownMenuItem + key={getKey(item.label, index, item.testID)} + onSelect={item.onPress}> + <Text + selectable={false} + style={[pal.text, styles.itemTitle]}> + {item.label} + </Text> + {item.icon && ( + <FontAwesomeIcon + icon={item.icon.web} + size={20} + color={pal.colors.textLight} + /> + )} + </DropdownMenuItem> + </DropdownMenu.Group> + ) + } + return ( + <DropdownMenuItem + key={getKey(item.label, index, item.testID)} + onSelect={item.onPress}> + <Text selectable={false} style={[pal.text, styles.itemTitle]}> + {item.label} + </Text> + {item.icon && ( + <FontAwesomeIcon + icon={item.icon.web} + size={20} + color={pal.colors.textLight} + /> + )} + </DropdownMenuItem> + ) + })} + </DropdownMenu.Content> + </DropdownMenu.Portal> + </DropdownMenuRoot> + ) +} + +const getKey = (label: string, index: number, id?: string) => { + if (id) { + return id + } + return `${label}_${index}` +} + +const styles = StyleSheet.create({ + separator: { + height: 1, + marginTop: 4, + marginBottom: 4, + }, + content: { + backgroundColor: '#f0f0f0', + borderRadius: 8, + paddingTop: 4, + paddingBottom: 4, + paddingLeft: 4, + paddingRight: 4, + marginTop: 6, + + // @ts-ignore web only -prf + boxShadow: 'rgba(0, 0, 0, 0.3) 0px 5px 20px', + }, + item: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + columnGap: 20, + // @ts-ignore -web + cursor: 'pointer', + paddingTop: 8, + paddingBottom: 8, + paddingLeft: 12, + paddingRight: 12, + borderRadius: 8, + }, + itemTitle: { + fontSize: 16, + fontWeight: '500', + paddingRight: 10, + }, +}) diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 193bb9bd7..1f2e067c2 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -18,6 +18,7 @@ import {getTranslatorLink} from '#/locale/helpers' import {usePostDeleteMutation} from '#/state/queries/post' import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' import {useLanguagePrefs} from '#/state/preferences' +import {useHiddenPosts, useHiddenPostsApi} from '#/state/preferences' import {logger} from '#/logger' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -50,9 +51,12 @@ let PostDropdownBtn = ({ const mutedThreads = useMutedThreads() const toggleThreadMute = useToggleThreadMute() const postDeleteMutation = usePostDeleteMutation() + const hiddenPosts = useHiddenPosts() + const {hidePost} = useHiddenPostsApi() const rootUri = record.reply?.root?.uri || postUri const isThreadMuted = mutedThreads.includes(rootUri) + const isPostHidden = hiddenPosts && hiddenPosts.includes(postUri) const isAuthor = postAuthor.did === currentAccount?.did const href = React.useMemo(() => { const urip = new AtUri(postUri) @@ -98,6 +102,10 @@ let PostDropdownBtn = ({ Linking.openURL(translatorUrl) }, [translatorUrl]) + const onHidePost = React.useCallback(() => { + hidePost({uri: postUri}) + }, [postUri, hidePost]) + const dropdownItems: NativeDropdownItem[] = [ { label: _(msg`Translate`), @@ -159,6 +167,27 @@ let PostDropdownBtn = ({ web: 'comment-slash', }, }, + hasSession && + !isAuthor && + !isPostHidden && { + label: _(msg`Hide post`), + onPress() { + openModal({ + name: 'confirm', + title: _(msg`Hide this post?`), + message: _(msg`This will hide this post from your feeds.`), + onPressConfirm: onHidePost, + }) + }, + testID: 'postDropdownHideBtn', + icon: { + ios: { + name: 'eye.slash', + }, + android: 'ic_menu_delete', + web: ['far', 'eye-slash'], + }, + }, { label: 'separator', }, diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index 414fb1e09..a50b52175 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -31,12 +31,14 @@ let PostCtrls = ({ big, post, record, + showAppealLabelItem, style, onPressReply, }: { big?: boolean post: Shadow<AppBskyFeedDefs.PostView> record: AppBskyFeedPost.Record + showAppealLabelItem?: boolean style?: StyleProp<ViewStyle> onPressReply: () => void }): React.ReactNode => { @@ -207,6 +209,7 @@ let PostCtrls = ({ postCid={post.cid} postUri={post.uri} record={record} + showAppealLabelItem={showAppealLabelItem} style={styles.ctrlPad} /> )} diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index d5bb38fb2..27aa804d3 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -6,22 +6,28 @@ import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 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' export const ExternalLinkEmbed = ({ link, - imageChild, }: { link: AppBskyEmbedExternal.ViewExternal - imageChild?: React.ReactNode }) => { const pal = usePalette('default') const {isMobile} = useWebMediaQueries() + + const embedPlayerParams = React.useMemo( + () => parseEmbedPlayerFromUrl(link.uri), + [link.uri], + ) + return ( <View style={{ - flexDirection: isMobile ? 'column' : 'row', + flexDirection: !isMobile && !embedPlayerParams ? 'row' : 'column', }}> - {link.thumb ? ( + {link.thumb && !embedPlayerParams ? ( <View style={ !isMobile @@ -45,9 +51,11 @@ export const ExternalLinkEmbed = ({ source={{uri: link.thumb}} accessibilityIgnoresInvertColors /> - {imageChild} </View> ) : undefined} + {embedPlayerParams && ( + <ExternalPlayer link={link} params={embedPlayerParams} /> + )} <View style={{ paddingHorizontal: isMobile ? 10 : 14, diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx new file mode 100644 index 000000000..580cf363a --- /dev/null +++ b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx @@ -0,0 +1,251 @@ +import React from 'react' +import { + ActivityIndicator, + Dimensions, + GestureResponderEvent, + Pressable, + StyleSheet, + View, +} from 'react-native' +import {Image} from 'expo-image' +import {WebView} from 'react-native-webview' +import YoutubePlayer from 'react-native-youtube-iframe' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {EmbedPlayerParams, getPlayerHeight} from 'lib/strings/embed-player' +import {EventStopper} from '../EventStopper' +import {AppBskyEmbedExternal} from '@atproto/api' +import {isNative} from 'platform/detection' +import {useNavigation} from '@react-navigation/native' +import {NavigationProp} from 'lib/routes/types' + +interface ShouldStartLoadRequest { + url: string +} + +// This renders the overlay when the player is either inactive or loading as a separate layer +function PlaceholderOverlay({ + isLoading, + isPlayerActive, + onPress, +}: { + isLoading: boolean + isPlayerActive: boolean + onPress: (event: GestureResponderEvent) => void +}) { + // If the player is active and not loading, we don't want to show the overlay. + if (isPlayerActive && !isLoading) return null + + return ( + <View style={[styles.layer, styles.overlayLayer]}> + <Pressable + accessibilityRole="button" + accessibilityLabel="Play Video" + accessibilityHint="" + onPress={onPress} + style={[styles.overlayContainer, styles.topRadius]}> + {!isPlayerActive ? ( + <FontAwesomeIcon icon="play" size={42} color="white" /> + ) : ( + <ActivityIndicator size="large" color="white" /> + )} + </Pressable> + </View> + ) +} + +// This renders the webview/youtube player as a separate layer +function Player({ + height, + params, + onLoad, + isPlayerActive, +}: { + isPlayerActive: boolean + params: EmbedPlayerParams + height: number + onLoad: () => void +}) { + // ensures we only load what's requested + const onShouldStartLoadWithRequest = React.useCallback( + (event: ShouldStartLoadRequest) => event.url === params.playerUri, + [params.playerUri], + ) + + // Don't show the player until it is active + if (!isPlayerActive) return null + + return ( + <View style={[styles.layer, styles.playerLayer]}> + <EventStopper> + {isNative && params.type === 'youtube_video' ? ( + <YoutubePlayer + videoId={params.videoId} + play + height={height} + onReady={onLoad} + webViewStyle={[styles.webview, styles.topRadius]} + /> + ) : ( + <View style={{height, width: '100%'}}> + <WebView + javaScriptEnabled={true} + onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} + mediaPlaybackRequiresUserAction={false} + allowsInlineMediaPlayback + bounces={false} + allowsFullscreenVideo + nestedScrollEnabled + source={{uri: params.playerUri}} + onLoad={onLoad} + setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads) + style={[styles.webview, styles.topRadius]} + /> + </View> + )} + </EventStopper> + </View> + ) +} + +// This renders the player area and handles the logic for when to show the player and when to show the overlay +export function ExternalPlayer({ + link, + params, +}: { + link: AppBskyEmbedExternal.ViewExternal + params: EmbedPlayerParams +}) { + const navigation = useNavigation<NavigationProp>() + + const [isPlayerActive, setPlayerActive] = React.useState(false) + const [isLoading, setIsLoading] = React.useState(true) + const [dim, setDim] = React.useState({ + width: 0, + height: 0, + }) + + const viewRef = React.useRef<View>(null) + + // watch for leaving the viewport due to scrolling + React.useEffect(() => { + // Interval for scrolling works in most cases, However, for twitch embeds, if we navigate away from the screen the webview will + // continue playing. We need to watch for the blur event + const unsubscribe = navigation.addListener('blur', () => { + setPlayerActive(false) + }) + + const interval = setInterval(() => { + viewRef.current?.measure((x, y, w, h, pageX, pageY) => { + const window = Dimensions.get('window') + const top = pageY + const bot = pageY + h + const isVisible = isNative + ? top >= 0 && bot <= window.height + : !(top >= window.height || bot <= 0) + if (!isVisible) { + setPlayerActive(false) + } + }) + }, 1e3) + return () => { + unsubscribe() + clearInterval(interval) + } + }, [viewRef, navigation]) + + // calculate height for the player and the screen size + const height = React.useMemo( + () => + getPlayerHeight({ + type: params.type, + width: dim.width, + hasThumb: !!link.thumb, + }), + [params.type, dim.width, link.thumb], + ) + + const onLoad = React.useCallback(() => { + setIsLoading(false) + }, []) + + const onPlayPress = React.useCallback((event: GestureResponderEvent) => { + // Prevent this from propagating upward on web + event.preventDefault() + + setPlayerActive(true) + }, []) + + // measure the layout to set sizing + const onLayout = React.useCallback( + (event: {nativeEvent: {layout: {width: any; height: any}}}) => { + setDim({ + width: event.nativeEvent.layout.width, + height: event.nativeEvent.layout.height, + }) + }, + [], + ) + + return ( + <View + ref={viewRef} + style={{height}} + collapsable={false} + onLayout={onLayout}> + {link.thumb && (!isPlayerActive || isLoading) && ( + <Image + style={[ + { + width: dim.width, + height, + }, + styles.topRadius, + ]} + source={{uri: link.thumb}} + accessibilityIgnoresInvertColors + /> + )} + + <PlaceholderOverlay + isLoading={isLoading} + isPlayerActive={isPlayerActive} + onPress={onPlayPress} + /> + <Player + isPlayerActive={isPlayerActive} + params={params} + height={height} + onLoad={onLoad} + /> + </View> + ) +} + +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)', + }, + overlayLayer: { + zIndex: 2, + }, + playerLayer: { + zIndex: 3, + }, + webview: { + backgroundColor: 'transparent', + }, +}) diff --git a/src/view/com/util/post-embeds/YoutubeEmbed.tsx b/src/view/com/util/post-embeds/YoutubeEmbed.tsx deleted file mode 100644 index 2f2da5662..000000000 --- a/src/view/com/util/post-embeds/YoutubeEmbed.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' -import {usePalette} from 'lib/hooks/usePalette' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {ExternalLinkEmbed} from './ExternalLinkEmbed' -import {AppBskyEmbedExternal} from '@atproto/api' -import {Link} from '../Link' - -export const YoutubeEmbed = ({ - link, - style, -}: { - link: AppBskyEmbedExternal.ViewExternal - style?: StyleProp<ViewStyle> -}) => { - const pal = usePalette('default') - - const imageChild = ( - <View style={styles.playButton}> - <FontAwesomeIcon icon="play" size={24} color="white" /> - </View> - ) - - return ( - <Link - asAnchor - style={[styles.extOuter, pal.view, pal.border, style]} - href={link.uri}> - <ExternalLinkEmbed link={link} imageChild={imageChild} /> - </Link> - ) -} - -const styles = StyleSheet.create({ - extOuter: { - borderWidth: 1, - borderRadius: 8, - }, - playButton: { - position: 'absolute', - alignSelf: 'center', - alignItems: 'center', - top: '44%', - justifyContent: 'center', - backgroundColor: 'black', - padding: 10, - borderRadius: 50, - opacity: 0.8, - }, - webView: { - alignItems: 'center', - alignContent: 'center', - justifyContent: 'center', - }, -}) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 2814cad87..c94ce9684 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -23,9 +23,7 @@ import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {useLightboxControls, ImagesLightbox} from '#/state/lightbox' import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {YoutubeEmbed} from './YoutubeEmbed' import {ExternalLinkEmbed} from './ExternalLinkEmbed' -import {getYoutubeVideoId} from 'lib/strings/url-helpers' import {MaybeQuoteEmbed} from './QuoteEmbed' import {AutoSizedImage} from '../images/AutoSizedImage' import {ListEmbed} from './ListEmbed' @@ -168,19 +166,13 @@ export function PostEmbeds({ // = if (AppBskyEmbedExternal.isView(embed)) { const link = embed.external - const youtubeVideoId = getYoutubeVideoId(link.uri) - - if (youtubeVideoId) { - return <YoutubeEmbed link={link} style={style} /> - } return ( - <Link - asAnchor - style={[styles.extOuter, pal.view, pal.border, style]} - href={link.uri}> - <ExternalLinkEmbed link={link} /> - </Link> + <View style={[styles.extOuter, pal.view, pal.border, style]}> + <Link asAnchor href={link.uri}> + <ExternalLinkEmbed link={link} /> + </Link> + </View> ) } |