Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
122
fork

Configure Feed

Select the types of activity you want to include in your feed.

Player improvement pre-reqs (#3618)

* add dims to type

* save

* add the dimensions to the embed info

* add a new case

* add a new case

* limit this case to giphy

* use gate

* flip mp4/webp

* fix tests

* add new test

authored by

Hailey and committed by
GitHub
c0ca8915 22e86c99

+89 -43
+36 -20
__tests__/lib/string.test.ts
··· 1 1 import {RichText} from '@atproto/api' 2 + 3 + import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' 4 + import {cleanError} from '../../src/lib/strings/errors' 5 + import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles' 6 + import {enforceLen, pluralize} from '../../src/lib/strings/helpers' 7 + import {detectLinkables} from '../../src/lib/strings/rich-text-detection' 8 + import {shortenLinks} from '../../src/lib/strings/rich-text-manip' 9 + import {ago} from '../../src/lib/strings/time' 2 10 import { 3 11 makeRecordUri, 4 12 toNiceDomain, 13 + toShareUrl, 5 14 toShortUrl, 6 - toShareUrl, 7 15 } from '../../src/lib/strings/url-helpers' 8 - import {pluralize, enforceLen} from '../../src/lib/strings/helpers' 9 - import {ago} from '../../src/lib/strings/time' 10 - import {detectLinkables} from '../../src/lib/strings/rich-text-detection' 11 - import {shortenLinks} from '../../src/lib/strings/rich-text-manip' 12 - import {makeValidHandle, createFullHandle} from '../../src/lib/strings/handles' 13 - import {cleanError} from '../../src/lib/strings/errors' 14 - import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' 15 16 16 17 describe('detectLinkables', () => { 17 18 const inputs = [ ··· 434 435 'https://giphy.com/gif/some-random-gif-name-gifId', 435 436 'https://giphy.com/gifs/', 436 437 438 + 'https://giphy.com/gifs/39248209509382934029?hh=100&ww=100', 439 + 437 440 'https://media.giphy.com/media/gifId/giphy.webp', 438 441 'https://media0.giphy.com/media/gifId/giphy.webp', 439 442 'https://media1.giphy.com/media/gifId/giphy.gif', ··· 621 624 isGif: true, 622 625 hideDetails: true, 623 626 metaUri: 'https://giphy.com/gifs/gifId', 624 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 627 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 625 628 }, 626 629 undefined, 627 630 undefined, ··· 631 634 source: 'giphy', 632 635 isGif: true, 633 636 hideDetails: true, 637 + metaUri: 'https://giphy.com/gifs/39248209509382934029', 638 + playerUri: 'https://i.giphy.com/media/39248209509382934029/200.mp4', 639 + dimensions: { 640 + width: 100, 641 + height: 100, 642 + }, 643 + }, 644 + 645 + { 646 + type: 'giphy_gif', 647 + source: 'giphy', 648 + isGif: true, 649 + hideDetails: true, 634 650 metaUri: 'https://giphy.com/gifs/gifId', 635 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 651 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 636 652 }, 637 653 { 638 654 type: 'giphy_gif', ··· 640 656 isGif: true, 641 657 hideDetails: true, 642 658 metaUri: 'https://giphy.com/gifs/gifId', 643 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 659 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 644 660 }, 645 661 { 646 662 type: 'giphy_gif', ··· 648 664 isGif: true, 649 665 hideDetails: true, 650 666 metaUri: 'https://giphy.com/gifs/gifId', 651 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 667 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 652 668 }, 653 669 { 654 670 type: 'giphy_gif', ··· 656 672 isGif: true, 657 673 hideDetails: true, 658 674 metaUri: 'https://giphy.com/gifs/gifId', 659 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 675 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 660 676 }, 661 677 { 662 678 type: 'giphy_gif', ··· 664 680 isGif: true, 665 681 hideDetails: true, 666 682 metaUri: 'https://giphy.com/gifs/gifId', 667 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 683 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 668 684 }, 669 685 { 670 686 type: 'giphy_gif', ··· 672 688 isGif: true, 673 689 hideDetails: true, 674 690 metaUri: 'https://giphy.com/gifs/gifId', 675 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 691 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 676 692 }, 677 693 undefined, 678 694 undefined, ··· 684 700 isGif: true, 685 701 hideDetails: true, 686 702 metaUri: 'https://giphy.com/gifs/gifId', 687 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 703 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 688 704 }, 689 705 690 706 { ··· 693 709 isGif: true, 694 710 hideDetails: true, 695 711 metaUri: 'https://giphy.com/gifs/gifId', 696 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 712 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 697 713 }, 698 714 { 699 715 type: 'giphy_gif', ··· 701 717 isGif: true, 702 718 hideDetails: true, 703 719 metaUri: 'https://giphy.com/gifs/gifId', 704 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 720 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 705 721 }, 706 722 { 707 723 type: 'giphy_gif', ··· 709 725 isGif: true, 710 726 hideDetails: true, 711 727 metaUri: 'https://giphy.com/gifs/gifId', 712 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 728 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 713 729 }, 714 730 { 715 731 type: 'giphy_gif', ··· 717 733 isGif: true, 718 734 hideDetails: true, 719 735 metaUri: 'https://giphy.com/gifs/gifId', 720 - playerUri: 'https://i.giphy.com/media/gifId/giphy.webp', 736 + playerUri: 'https://i.giphy.com/media/gifId/200.webp', 721 737 }, 722 738 723 739 {
+1
src/lib/statsig/gates.ts
··· 4 4 | 'disable_min_shell_on_foregrounding_v2' 5 5 | 'disable_poll_on_discover_v2' 6 6 | 'hide_vertical_scroll_indicators' 7 + | 'new_gif_player' 7 8 | 'new_profile_scroll_component' 8 9 | 'receive_updates' 9 10 | 'show_follow_back_label_v2'
+23 -5
src/lib/strings/embed-player.ts
··· 1 1 import {Dimensions} from 'react-native' 2 + 2 3 import {isWeb} from 'platform/detection' 3 4 const {height: SCREEN_HEIGHT} = Dimensions.get('window') 4 5 ··· 60 61 source: EmbedPlayerSource 61 62 metaUri?: string 62 63 hideDetails?: boolean 64 + dimensions?: { 65 + height: number 66 + width: number 67 + } 63 68 } 64 69 65 70 const giphyRegex = /media(?:[0-4]\.giphy\.com|\.giphy\.com)/i ··· 250 255 if (urlp.hostname === 'giphy.com' || urlp.hostname === 'www.giphy.com') { 251 256 const [_, gifs, nameAndId] = urlp.pathname.split('/') 252 257 258 + const h = urlp.searchParams.get('hh') 259 + const w = urlp.searchParams.get('ww') 260 + let dimensions 261 + if (h && w) { 262 + dimensions = { 263 + height: Number(h), 264 + width: Number(w), 265 + } 266 + } 267 + 253 268 /* 254 269 * nameAndId is a string that consists of the name (dash separated) and the id of the gif (the last part of the name) 255 270 * We want to get the id of the gif, then direct to media.giphy.com/media/{id}/giphy.webp so we can ··· 266 281 isGif: true, 267 282 hideDetails: true, 268 283 metaUri: `https://giphy.com/gifs/${gifId}`, 269 - playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, 284 + playerUri: `https://i.giphy.com/media/${gifId}/${ 285 + dimensions ? '200.mp4' : '200.webp' 286 + }`, 287 + dimensions, 270 288 } 271 289 } 272 290 } ··· 287 305 isGif: true, 288 306 hideDetails: true, 289 307 metaUri: `https://giphy.com/gifs/${trackingOrId}`, 290 - playerUri: `https://i.giphy.com/media/${trackingOrId}/giphy.webp`, 308 + playerUri: `https://i.giphy.com/media/${trackingOrId}/200.webp`, 291 309 } 292 310 } else if (filename && gifFilenameRegex.test(filename)) { 293 311 return { ··· 296 314 isGif: true, 297 315 hideDetails: true, 298 316 metaUri: `https://giphy.com/gifs/${idOrFilename}`, 299 - playerUri: `https://i.giphy.com/media/${idOrFilename}/giphy.webp`, 317 + playerUri: `https://i.giphy.com/media/${idOrFilename}/200.webp`, 300 318 } 301 319 } 302 320 } ··· 315 333 isGif: true, 316 334 hideDetails: true, 317 335 metaUri: `https://giphy.com/gifs/${gifId}`, 318 - playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, 336 + playerUri: `https://i.giphy.com/media/${gifId}/200.webp`, 319 337 } 320 338 } else if (mediaOrFilename) { 321 339 const gifId = mediaOrFilename.split('.')[0] ··· 327 345 metaUri: `https://giphy.com/gifs/${gifId}`, 328 346 playerUri: `https://i.giphy.com/media/${ 329 347 mediaOrFilename.split('.')[0] 330 - }/giphy.webp`, 348 + }/200.webp`, 331 349 } 332 350 } 333 351 }
+29 -18
src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
··· 1 1 import React from 'react' 2 + import {StyleSheet, View} from 'react-native' 2 3 import {Image} from 'expo-image' 3 - import {Text} from '../text/Text' 4 - import {StyleSheet, View} from 'react-native' 4 + import {AppBskyEmbedExternal} from '@atproto/api' 5 + 5 6 import {usePalette} from 'lib/hooks/usePalette' 6 7 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 7 - import {AppBskyEmbedExternal} from '@atproto/api' 8 - import {toNiceDomain} from 'lib/strings/url-helpers' 8 + import {useGate} from 'lib/statsig/statsig' 9 9 import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' 10 - import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed' 10 + import {toNiceDomain} from 'lib/strings/url-helpers' 11 + import {useExternalEmbedsPrefs} from 'state/preferences' 11 12 import {ExternalGifEmbed} from 'view/com/util/post-embeds/ExternalGifEmbed' 12 - import {useExternalEmbedsPrefs} from 'state/preferences' 13 + import {ExternalPlayer} from 'view/com/util/post-embeds/ExternalPlayerEmbed' 14 + import {Text} from '../text/Text' 13 15 14 16 export const ExternalLinkEmbed = ({ 15 17 link, ··· 19 21 const pal = usePalette('default') 20 22 const {isMobile} = useWebMediaQueries() 21 23 const externalEmbedPrefs = useExternalEmbedsPrefs() 24 + const gate = useGate() 22 25 23 26 const embedPlayerParams = React.useMemo(() => { 24 27 const params = parseEmbedPlayerFromUrl(link.uri) ··· 27 30 return params 28 31 } 29 32 }, [link.uri, externalEmbedPrefs]) 33 + const isCompatibleGiphy = 34 + embedPlayerParams?.source === 'giphy' && 35 + embedPlayerParams.dimensions && 36 + gate('new_gif_player') 30 37 31 38 return ( 32 39 <View style={styles.container}> ··· 37 44 accessibilityIgnoresInvertColors 38 45 /> 39 46 ) : undefined} 40 - {(embedPlayerParams?.isGif && ( 47 + {isCompatibleGiphy ? ( 48 + <View /> 49 + ) : embedPlayerParams?.isGif ? ( 41 50 <ExternalGifEmbed link={link} params={embedPlayerParams} /> 42 - )) || 43 - (embedPlayerParams && ( 44 - <ExternalPlayer link={link} params={embedPlayerParams} /> 45 - ))} 51 + ) : embedPlayerParams ? ( 52 + <ExternalPlayer link={link} params={embedPlayerParams} /> 53 + ) : undefined} 46 54 <View style={[styles.info, {paddingHorizontal: isMobile ? 10 : 14}]}> 47 - <Text 48 - type="sm" 49 - numberOfLines={1} 50 - style={[pal.textLight, styles.extUri]}> 51 - {toNiceDomain(link.uri)} 52 - </Text> 53 - {!embedPlayerParams?.isGif && ( 55 + {!isCompatibleGiphy && ( 56 + <Text 57 + type="sm" 58 + numberOfLines={1} 59 + style={[pal.textLight, styles.extUri]}> 60 + {toNiceDomain(link.uri)} 61 + </Text> 62 + )} 63 + 64 + {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( 54 65 <Text type="lg-bold" numberOfLines={3} style={[pal.text]}> 55 66 {link.title || link.uri} 56 67 </Text>