diff options
author | Eric Bailey <git@esb.lol> | 2024-10-14 16:06:23 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-14 16:06:23 -0500 |
commit | 4c3c10d7f892777e48faccd534441ac7d88df042 (patch) | |
tree | 1d33ddaea5f4a7365325c5c951344dd994d1c8b4 /src | |
parent | 2d88463453abfad1e9e45bbd6cdbcd5824a7e770 (diff) | |
download | voidsky-4c3c10d7f892777e48faccd534441ac7d88df042.tar.zst |
Link cards (#5677)
* New link card styles * Cleanup of consituent parts, add hover state * Fix gif alt text view * Fix alt text view more * Remove dupe * Update remove button * Remove added margin on gif
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/composer/ExternalEmbedRemoveBtn.tsx | 38 | ||||
-rw-r--r-- | src/view/com/composer/GifAltText.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/ExternalGifEmbed.tsx | 40 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/ExternalLinkEmbed.tsx | 219 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx | 55 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/GifEmbed.tsx | 28 |
6 files changed, 153 insertions, 231 deletions
diff --git a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx b/src/view/com/composer/ExternalEmbedRemoveBtn.tsx index 57ccc2943..0dfa3ce09 100644 --- a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx +++ b/src/view/com/composer/ExternalEmbedRemoveBtn.tsx @@ -1,34 +1,26 @@ import React from 'react' -import {TouchableOpacity} from 'react-native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {s} from 'lib/styles' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon} from '#/components/Button' +import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' export function ExternalEmbedRemoveBtn({onRemove}: {onRemove: () => void}) { const {_} = useLingui() return ( - <TouchableOpacity - style={{ - position: 'absolute', - top: 10, - right: 10, - height: 36, - width: 36, - backgroundColor: 'rgba(0, 0, 0, 0.75)', - borderRadius: 18, - alignItems: 'center', - justifyContent: 'center', - zIndex: 1, - }} - onPress={onRemove} - accessibilityRole="button" - accessibilityLabel={_(msg`Remove attachment`)} - accessibilityHint={_(msg`Removes the attachment`)} - onAccessibilityEscape={onRemove}> - <FontAwesomeIcon size={18} icon="xmark" style={s.white} /> - </TouchableOpacity> + <View style={[a.absolute, a.pt_sm, a.pr_sm, {top: 0, right: 0}]}> + <Button + label={_(msg`Remove attachment`)} + onPress={onRemove} + size="small" + variant="solid" + color="secondary" + shape="round"> + <ButtonIcon icon={X} size="sm" /> + </Button> + </View> ) } diff --git a/src/view/com/composer/GifAltText.tsx b/src/view/com/composer/GifAltText.tsx index 732bd4bd6..143d7b826 100644 --- a/src/view/com/composer/GifAltText.tsx +++ b/src/view/com/composer/GifAltText.tsx @@ -13,7 +13,7 @@ import {isAndroid} from '#/platform/detection' import {useResolveGifQuery} from '#/state/queries/resolve-link' import {Gif} from '#/state/queries/tenor' import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' -import {atoms as a, native, useTheme} from '#/alf' +import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {DialogControlProps} from '#/components/Dialog' @@ -213,7 +213,7 @@ function AltTextInner({ isPreferredAltText={true} params={params} hideAlt - style={[native({maxHeight: 225})]} + style={[{height: 225}]} /> </View> </View> diff --git a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx b/src/view/com/util/post-embeds/ExternalGifEmbed.tsx index 6f1c88dcd..6db4d6fef 100644 --- a/src/view/com/util/post-embeds/ExternalGifEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalGifEmbed.tsx @@ -4,7 +4,6 @@ import { GestureResponderEvent, LayoutChangeEvent, Pressable, - StyleSheet, } from 'react-native' import {Image, ImageLoadEventData} from 'expo-image' import {AppBskyEmbedExternal} from '@atproto/api' @@ -18,7 +17,6 @@ import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {Fill} from '#/components/Fill' -import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' export function ExternalGifEmbed({ @@ -116,8 +114,7 @@ export function ExternalGifEmbed({ <Pressable style={[ {height: imageDims.height}, - styles.gifContainer, - a.rounded_md, + a.w_full, a.overflow_hidden, { borderBottomLeftRadius: 0, @@ -166,42 +163,7 @@ export function ExternalGifEmbed({ )} </Fill> )} - <MediaInsetBorder - opaque - style={[ - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]} - /> </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', - }, - overlayLayer: { - zIndex: 2, - }, - gifContainer: { - width: '100%', - overflow: 'hidden', - }, -}) diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index eb03385d0..0399667b0 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -6,8 +6,6 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {parseAltFromGIFDescription} from '#/lib/gif-alt-text' -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {shareUrl} from '#/lib/sharing' import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import { @@ -17,13 +15,14 @@ import { import {toNiceDomain} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' import {useExternalEmbedsPrefs} from '#/state/preferences' -import {Link} from '#/view/com/util/Link' import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed' import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed' import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed' import {atoms as a, useTheme} from '#/alf' -import {MediaInsetBorder} from '#/components/MediaInsetBorder' -import {Text} from '../text/Text' +import {Divider} from '#/components/Divider' +import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' +import {Link} from '#/components/Link' +import {Text} from '#/components/Typography' export const ExternalLinkEmbed = ({ link, @@ -37,16 +36,13 @@ export const ExternalLinkEmbed = ({ hideAlt?: boolean }) => { const {_} = useLingui() - const pal = usePalette('default') const t = useTheme() - const {isMobile} = useWebMediaQueries() const externalEmbedPrefs = useExternalEmbedsPrefs() - + const niceUrl = toNiceDomain(link.uri) const starterPackParsed = parseStarterPackUri(link.uri) const imageUri = starterPackParsed ? getStarterPackOgCard(starterPackParsed.name, starterPackParsed.rkey) : link.thumb - const embedPlayerParams = React.useMemo(() => { const params = parseEmbedPlayerFromUrl(link.uri) @@ -54,122 +50,131 @@ export const ExternalLinkEmbed = ({ return params } }, [link.uri, externalEmbedPrefs]) + const hasMedia = Boolean(imageUri || embedPlayerParams) + + const onShareExternal = useCallback(() => { + if (link.uri && isNative) { + shareUrl(link.uri) + } + }, [link.uri]) if (embedPlayerParams?.source === 'tenor') { const parsedAlt = parseAltFromGIFDescription(link.description) return ( - <GifEmbed - params={embedPlayerParams} - thumb={link.thumb} - altText={parsedAlt.alt} - isPreferredAltText={parsedAlt.isPreferred} - hideAlt={hideAlt} - /> + <View style={style}> + <GifEmbed + params={embedPlayerParams} + thumb={link.thumb} + altText={parsedAlt.alt} + isPreferredAltText={parsedAlt.isPreferred} + hideAlt={hideAlt} + /> + </View> ) } return ( - <View style={[a.flex_col, a.rounded_md, a.w_full]}> - <LinkWrapper link={link} onOpen={onOpen} style={style}> - {imageUri && !embedPlayerParams ? ( - <View> + <Link + label={link.title || _(msg`Open link to ${niceUrl}`)} + to={link.uri} + onPress={onOpen} + onLongPress={onShareExternal}> + {({hovered}) => ( + <View + style={[ + a.transition_color, + a.flex_col, + a.rounded_md, + a.overflow_hidden, + a.w_full, + a.border, + style, + hovered + ? t.atoms.border_contrast_high + : t.atoms.border_contrast_low, + ]}> + {imageUri && !embedPlayerParams ? ( <Image style={{ aspectRatio: 1.91, - borderTopRightRadius: a.rounded_md.borderRadius, - borderTopLeftRadius: a.rounded_md.borderRadius, }} source={{uri: imageUri}} accessibilityIgnoresInvertColors - accessibilityLabel={starterPackParsed ? link.title : undefined} - accessibilityHint={ - starterPackParsed ? _(msg`Navigate to starter pack`) : undefined - } /> - <MediaInsetBorder - opaque - style={[ - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]} - /> - </View> - ) : undefined} - {embedPlayerParams?.isGif ? ( - <ExternalGifEmbed link={link} params={embedPlayerParams} /> - ) : embedPlayerParams ? ( - <ExternalPlayer link={link} params={embedPlayerParams} /> - ) : undefined} - <View - style={[ - a.border_b, - a.border_l, - a.border_r, - a.flex_1, - a.py_sm, - t.atoms.border_contrast_low, - { - borderBottomRightRadius: a.rounded_md.borderRadius, - borderBottomLeftRadius: a.rounded_md.borderRadius, - paddingHorizontal: isMobile ? 10 : 14, - }, - !imageUri && !embedPlayerParams && [a.border, a.rounded_md], - ]}> - <Text - type="sm" - numberOfLines={1} - style={[pal.textLight, {marginVertical: 2}]}> - {toNiceDomain(link.uri)} - </Text> - - {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( - <Text emoji type="lg-bold" numberOfLines={3} style={[pal.text]}> - {link.title || link.uri} - </Text> - )} - {link.description ? ( - <Text - emoji - type="md" - numberOfLines={link.thumb ? 2 : 4} - style={[pal.text, a.mt_xs]}> - {link.description} - </Text> ) : undefined} - </View> - </LinkWrapper> - </View> - ) -} -function LinkWrapper({ - link, - onOpen, - style, - children, -}: { - link: AppBskyEmbedExternal.ViewExternal - onOpen?: () => void - style?: StyleProp<ViewStyle> - children: React.ReactNode -}) { - const onShareExternal = useCallback(() => { - if (link.uri && isNative) { - shareUrl(link.uri) - } - }, [link.uri]) + {embedPlayerParams?.isGif ? ( + <ExternalGifEmbed link={link} params={embedPlayerParams} /> + ) : embedPlayerParams ? ( + <ExternalPlayer link={link} params={embedPlayerParams} /> + ) : undefined} - return ( - <Link - asAnchor - anchorNoUnderline - href={link.uri} - style={[a.flex_1, a.rounded_sm, style]} - onBeforePress={onOpen} - onLongPress={onShareExternal}> - {children} + <View + style={[ + a.flex_1, + a.pt_sm, + {gap: 3}, + hasMedia && a.border_t, + hovered + ? t.atoms.border_contrast_high + : t.atoms.border_contrast_low, + ]}> + <View style={[{gap: 3}, a.pb_xs, a.px_md]}> + {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( + <Text + emoji + numberOfLines={3} + style={[a.text_md, a.font_bold, a.leading_snug]}> + {link.title || link.uri} + </Text> + )} + {link.description ? ( + <Text + emoji + numberOfLines={link.thumb ? 2 : 4} + style={[a.text_sm, a.leading_snug]}> + {link.description} + </Text> + ) : undefined} + </View> + <View style={[a.px_md]}> + <Divider /> + <View + style={[ + a.flex_row, + a.align_center, + a.gap_2xs, + a.pb_sm, + { + paddingTop: 6, // off menu + }, + ]}> + <Globe + size="xs" + style={[ + a.transition_color, + hovered + ? t.atoms.text_contrast_medium + : t.atoms.text_contrast_low, + ]} + /> + <Text + numberOfLines={1} + style={[ + a.transition_color, + a.text_xs, + a.leading_tight, + hovered + ? t.atoms.text_contrast_high + : t.atoms.text_contrast_medium, + ]}> + {toNiceDomain(link.uri)} + </Text> + </View> + </View> + </View> + </View> + )} </Link> ) } diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx index 6d5eacd1a..8ac7ee499 100644 --- a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx @@ -29,7 +29,6 @@ import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {Fill} from '#/components/Fill' -import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {EventStopper} from '../EventStopper' @@ -59,7 +58,7 @@ function PlaceholderOverlay({ accessibilityLabel={_(msg`Play Video`)} accessibilityHint={_(msg`Play Video`)} onPress={onPress} - style={[styles.overlayContainer, styles.topRadius]}> + style={[styles.overlayContainer]}> {!isPlayerActive ? ( <PlayButtonIcon /> ) : ( @@ -108,16 +107,6 @@ function Player({ style={styles.webview} setSupportMultipleWindows={false} // Prevent any redirects from opening a new window (ads) /> - - <MediaInsetBorder - opaque - style={[ - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]} - /> </EventStopper> ) } @@ -227,66 +216,34 @@ export function ExternalPlayer({ <Animated.View ref={viewRef} collapsable={false} - style={[ - aspect, - a.rounded_md, - a.overflow_hidden, - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]}> + style={[aspect, a.overflow_hidden]}> {link.thumb && (!isPlayerActive || isLoading) ? ( <> <Image - style={[a.flex_1, styles.topRadius]} + style={[a.flex_1]} source={{uri: link.thumb}} accessibilityIgnoresInvertColors /> <Fill style={[ - a.rounded_md, t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg, { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, opacity: 0.3, }, ]} /> - <MediaInsetBorder - opaque - style={[ - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]} - /> </> ) : ( <Fill style={[ - a.rounded_md, { backgroundColor: t.name === 'light' ? t.palette.contrast_975 : 'black', - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, opacity: 0.3, }, ]} /> )} - <MediaInsetBorder - opaque - style={[ - { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, - }, - ]} - /> <PlaceholderOverlay isLoading={isLoading} isPlayerActive={isPlayerActive} @@ -303,10 +260,6 @@ export function ExternalPlayer({ } const styles = StyleSheet.create({ - topRadius: { - borderTopLeftRadius: a.rounded_md.borderRadius, - borderTopRightRadius: a.rounded_md.borderRadius, - }, overlayContainer: { flex: 1, justifyContent: 'center', @@ -319,8 +272,6 @@ const styles = StyleSheet.create({ zIndex: 3, }, webview: { - borderTopRightRadius: a.rounded_md.borderRadius, - borderTopLeftRadius: a.rounded_md.borderRadius, backgroundColor: 'transparent', }, gifContainer: { diff --git a/src/view/com/util/post-embeds/GifEmbed.tsx b/src/view/com/util/post-embeds/GifEmbed.tsx index fc66278c9..4dbc7d588 100644 --- a/src/view/com/util/post-embeds/GifEmbed.tsx +++ b/src/view/com/util/post-embeds/GifEmbed.tsx @@ -18,7 +18,6 @@ import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useTheme} from '#/alf' import {Fill} from '#/components/Fill' import {Loader} from '#/components/Loader' -import {MediaInsetBorder} from '#/components/MediaInsetBorder' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' @@ -51,7 +50,6 @@ function PlaybackControls({ a.inset_0, a.w_full, a.h_full, - a.rounded_md, { zIndex: 2, backgroundColor: !isLoaded @@ -114,12 +112,27 @@ export function GifEmbed({ }, []) return ( - <View style={[a.rounded_md, a.overflow_hidden, a.mt_sm, style]}> + <View + style={[ + a.rounded_md, + a.overflow_hidden, + a.border, + t.atoms.border_contrast_low, + {aspectRatio: params.dimensions!.width / params.dimensions!.height}, + style, + ]}> <View style={[ - a.rounded_md, - a.overflow_hidden, - {aspectRatio: params.dimensions!.width / params.dimensions!.height}, + a.absolute, + /* + * Aspect ratio was being clipped weirdly on web -esb + */ + { + top: -2, + bottom: -2, + left: -2, + right: -2, + }, ]}> <PlaybackControls onPress={onPress} @@ -129,7 +142,7 @@ export function GifEmbed({ <GifView source={params.playerUri} placeholderSource={thumb} - style={[a.flex_1, a.rounded_md]} + style={[a.flex_1]} autoplay={!autoplayDisabled} onPlayerStateChange={onPlayerStateChange} ref={playerRef} @@ -146,7 +159,6 @@ export function GifEmbed({ ]} /> )} - <MediaInsetBorder /> {!hideAlt && isPreferredAltText && <AltText text={altText} />} </View> </View> |