Bluesky app fork with some witchin' additions 💫

Bandcamp embed (#9445)

authored by samuel.fm and committed by

GitHub c83dddce 79542897

+67 -14
+24
__tests__/lib/string.test.ts
··· 437 437 438 438 'https://www.flickr.com/groups/898944@N23/', 439 439 'https://www.flickr.com/groups', 440 + 441 + 'https://maxblansjaar.bandcamp.com/album/false-comforts', 442 + 'https://grmnygrmny.bandcamp.com/track/fluid', 443 + 'https://sufjanstevens.bandcamp.com/', 444 + 'https://sufjanstevens.bandcamp.com', 445 + 'https://bandcamp.com/', 446 + 'https://bandcamp.com', 440 447 ] 441 448 442 449 const outputs = [ ··· 813 820 playerUri: 'https://embedr.flickr.com/groups/898944@N23', 814 821 }, 815 822 823 + undefined, 824 + undefined, 825 + 826 + { 827 + type: 'bandcamp_album', 828 + source: 'bandcamp', 829 + playerUri: 830 + 'https://bandcamp.com/EmbeddedPlayer/url=https%3A%2F%2Fmaxblansjaar.bandcamp.com%2Falbum%2Ffalse-comforts/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/', 831 + }, 832 + { 833 + type: 'bandcamp_track', 834 + source: 'bandcamp', 835 + playerUri: 836 + 'https://bandcamp.com/EmbeddedPlayer/url=https%3A%2F%2Fgrmnygrmny.bandcamp.com%2Ftrack%2Ffluid/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/', 837 + }, 838 + undefined, 839 + undefined, 816 840 undefined, 817 841 undefined, 818 842 ]
+9 -14
src/components/dialogs/EmbedConsent.tsx
··· 9 9 externalEmbedLabels, 10 10 } from '#/lib/strings/embed-player' 11 11 import {useSetExternalEmbedPref} from '#/state/preferences' 12 - import {atoms as a, useBreakpoints, useTheme} from '#/alf' 12 + import {atoms as a, web} from '#/alf' 13 + import {Admonition} from '#/components/Admonition' 13 14 import {Button, ButtonText} from '#/components/Button' 14 15 import * as Dialog from '#/components/Dialog' 15 16 import {Text} from '#/components/Typography' ··· 24 25 onAccept: () => void 25 26 }) { 26 27 const {_} = useLingui() 27 - const t = useTheme() 28 28 const setExternalEmbedPref = useSetExternalEmbedPref() 29 - const {gtMobile} = useBreakpoints() 30 29 31 30 const onShowAllPress = useCallback(() => { 32 31 for (const key of embedPlayerSources) { ··· 52 51 <Dialog.Handle /> 53 52 <Dialog.ScrollableInner 54 53 label={_(msg`External Media`)} 55 - style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}> 54 + style={web({maxWidth: 400})}> 56 55 <View style={a.gap_sm}> 57 - <Text style={[a.text_2xl, a.font_semi_bold]}> 56 + <Text style={[a.text_2xl, a.font_bold]}> 58 57 <Trans>External Media</Trans> 59 58 </Text> 60 59 61 60 <View style={[a.mt_sm, a.mb_2xl, a.gap_lg]}> 62 - <Text> 61 + <Text style={[a.text_md, a.leading_snug]}> 63 62 <Trans> 64 63 This content is hosted by {externalEmbedLabels[source]}. Do you 65 64 want to enable external media? 66 65 </Trans> 67 66 </Text> 68 67 69 - <Text style={t.atoms.text_contrast_medium}> 68 + <Admonition type="info"> 70 69 <Trans> 71 70 External media may allow websites to collect information about 72 71 you and your device. No information is sent or requested until 73 72 you press the "play" button. 74 73 </Trans> 75 - </Text> 74 + </Admonition> 76 75 </View> 77 76 </View> 78 77 <View style={a.gap_md}> 79 78 <Button 80 - style={gtMobile && a.flex_1} 81 79 label={_(msg`Enable external media`)} 82 80 onPress={onShowAllPress} 83 81 onAccessibilityEscape={control.close} 84 82 color="primary" 85 - size="large" 86 - variant="solid"> 83 + size="large"> 87 84 <ButtonText> 88 85 <Trans>Enable external media</Trans> 89 86 </ButtonText> 90 87 </Button> 91 88 <Button 92 - style={gtMobile && a.flex_1} 93 89 label={_(msg`Enable this source only`)} 94 90 onPress={onShowPress} 95 91 onAccessibilityEscape={control.close} 96 92 color="secondary" 97 - size="large" 98 - variant="solid"> 93 + size="large"> 99 94 <ButtonText> 100 95 <Trans>Enable {externalEmbedLabels[source]} only</Trans> 101 96 </ButtonText>
+33
src/lib/strings/embed-player.ts
··· 24 24 'giphy', 25 25 'tenor', 26 26 'flickr', 27 + 'bandcamp', 27 28 ] as const 28 29 29 30 export type EmbedPlayerSource = (typeof embedPlayerSources)[number] ··· 44 45 | 'giphy_gif' 45 46 | 'tenor_gif' 46 47 | 'flickr_album' 48 + | 'bandcamp_album' 49 + | 'bandcamp_track' 47 50 48 51 export const externalEmbedLabels: Record<EmbedPlayerSource, string> = { 49 52 youtube: 'YouTube', ··· 56 59 appleMusic: 'Apple Music', 57 60 soundcloud: 'SoundCloud', 58 61 flickr: 'Flickr', 62 + bandcamp: 'Bandcamp', 59 63 } 60 64 61 65 export interface EmbedPlayerParams { ··· 459 463 return undefined 460 464 } 461 465 } 466 + 467 + const bandcampRegex = /^[a-z\d][a-z\d-]{2,}[a-z\d]\.bandcamp\.com$/i 468 + 469 + if (bandcampRegex.test(urlp.hostname)) { 470 + const pathComponents = urlp.pathname.split('/') 471 + switch (pathComponents[1]) { 472 + case 'album': 473 + return { 474 + type: 'bandcamp_album', 475 + source: 'bandcamp', 476 + playerUri: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent( 477 + urlp.href, 478 + )}/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/`, 479 + } 480 + case 'track': 481 + return { 482 + type: 'bandcamp_track', 483 + source: 'bandcamp', 484 + playerUri: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent( 485 + urlp.href, 486 + )}/size=large/bgcol=ffffff/linkcol=0687f5/minimal=true/transparent=true/`, 487 + } 488 + default: 489 + return undefined 490 + } 491 + } 462 492 } 463 493 464 494 export function getPlayerAspect({ ··· 498 528 return {height: 165} 499 529 case 'apple_music_song': 500 530 return {height: 150} 531 + case 'bandcamp_album': 532 + case 'bandcamp_track': 533 + return {aspectRatio: 1} 501 534 default: 502 535 return {aspectRatio: 16 / 9} 503 536 }
+1
src/state/persisted/schema.ts
··· 106 106 appleMusic: z.enum(externalEmbedOptions).optional(), 107 107 soundcloud: z.enum(externalEmbedOptions).optional(), 108 108 flickr: z.enum(externalEmbedOptions).optional(), 109 + bandcamp: z.enum(externalEmbedOptions).optional(), 109 110 }) 110 111 .optional(), 111 112 invites: z.object({