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 438 'https://www.flickr.com/groups/898944@N23/', 439 'https://www.flickr.com/groups', 440 ] 441 442 const outputs = [ ··· 813 playerUri: 'https://embedr.flickr.com/groups/898944@N23', 814 }, 815 816 undefined, 817 undefined, 818 ]
··· 437 438 'https://www.flickr.com/groups/898944@N23/', 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', 447 ] 448 449 const outputs = [ ··· 820 playerUri: 'https://embedr.flickr.com/groups/898944@N23', 821 }, 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, 840 undefined, 841 undefined, 842 ]
+9 -14
src/components/dialogs/EmbedConsent.tsx
··· 9 externalEmbedLabels, 10 } from '#/lib/strings/embed-player' 11 import {useSetExternalEmbedPref} from '#/state/preferences' 12 - import {atoms as a, useBreakpoints, useTheme} from '#/alf' 13 import {Button, ButtonText} from '#/components/Button' 14 import * as Dialog from '#/components/Dialog' 15 import {Text} from '#/components/Typography' ··· 24 onAccept: () => void 25 }) { 26 const {_} = useLingui() 27 - const t = useTheme() 28 const setExternalEmbedPref = useSetExternalEmbedPref() 29 - const {gtMobile} = useBreakpoints() 30 31 const onShowAllPress = useCallback(() => { 32 for (const key of embedPlayerSources) { ··· 52 <Dialog.Handle /> 53 <Dialog.ScrollableInner 54 label={_(msg`External Media`)} 55 - style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}> 56 <View style={a.gap_sm}> 57 - <Text style={[a.text_2xl, a.font_semi_bold]}> 58 <Trans>External Media</Trans> 59 </Text> 60 61 <View style={[a.mt_sm, a.mb_2xl, a.gap_lg]}> 62 - <Text> 63 <Trans> 64 This content is hosted by {externalEmbedLabels[source]}. Do you 65 want to enable external media? 66 </Trans> 67 </Text> 68 69 - <Text style={t.atoms.text_contrast_medium}> 70 <Trans> 71 External media may allow websites to collect information about 72 you and your device. No information is sent or requested until 73 you press the "play" button. 74 </Trans> 75 - </Text> 76 </View> 77 </View> 78 <View style={a.gap_md}> 79 <Button 80 - style={gtMobile && a.flex_1} 81 label={_(msg`Enable external media`)} 82 onPress={onShowAllPress} 83 onAccessibilityEscape={control.close} 84 color="primary" 85 - size="large" 86 - variant="solid"> 87 <ButtonText> 88 <Trans>Enable external media</Trans> 89 </ButtonText> 90 </Button> 91 <Button 92 - style={gtMobile && a.flex_1} 93 label={_(msg`Enable this source only`)} 94 onPress={onShowPress} 95 onAccessibilityEscape={control.close} 96 color="secondary" 97 - size="large" 98 - variant="solid"> 99 <ButtonText> 100 <Trans>Enable {externalEmbedLabels[source]} only</Trans> 101 </ButtonText>
··· 9 externalEmbedLabels, 10 } from '#/lib/strings/embed-player' 11 import {useSetExternalEmbedPref} from '#/state/preferences' 12 + import {atoms as a, web} from '#/alf' 13 + import {Admonition} from '#/components/Admonition' 14 import {Button, ButtonText} from '#/components/Button' 15 import * as Dialog from '#/components/Dialog' 16 import {Text} from '#/components/Typography' ··· 25 onAccept: () => void 26 }) { 27 const {_} = useLingui() 28 const setExternalEmbedPref = useSetExternalEmbedPref() 29 30 const onShowAllPress = useCallback(() => { 31 for (const key of embedPlayerSources) { ··· 51 <Dialog.Handle /> 52 <Dialog.ScrollableInner 53 label={_(msg`External Media`)} 54 + style={web({maxWidth: 400})}> 55 <View style={a.gap_sm}> 56 + <Text style={[a.text_2xl, a.font_bold]}> 57 <Trans>External Media</Trans> 58 </Text> 59 60 <View style={[a.mt_sm, a.mb_2xl, a.gap_lg]}> 61 + <Text style={[a.text_md, a.leading_snug]}> 62 <Trans> 63 This content is hosted by {externalEmbedLabels[source]}. Do you 64 want to enable external media? 65 </Trans> 66 </Text> 67 68 + <Admonition type="info"> 69 <Trans> 70 External media may allow websites to collect information about 71 you and your device. No information is sent or requested until 72 you press the "play" button. 73 </Trans> 74 + </Admonition> 75 </View> 76 </View> 77 <View style={a.gap_md}> 78 <Button 79 label={_(msg`Enable external media`)} 80 onPress={onShowAllPress} 81 onAccessibilityEscape={control.close} 82 color="primary" 83 + size="large"> 84 <ButtonText> 85 <Trans>Enable external media</Trans> 86 </ButtonText> 87 </Button> 88 <Button 89 label={_(msg`Enable this source only`)} 90 onPress={onShowPress} 91 onAccessibilityEscape={control.close} 92 color="secondary" 93 + size="large"> 94 <ButtonText> 95 <Trans>Enable {externalEmbedLabels[source]} only</Trans> 96 </ButtonText>
+33
src/lib/strings/embed-player.ts
··· 24 'giphy', 25 'tenor', 26 'flickr', 27 ] as const 28 29 export type EmbedPlayerSource = (typeof embedPlayerSources)[number] ··· 44 | 'giphy_gif' 45 | 'tenor_gif' 46 | 'flickr_album' 47 48 export const externalEmbedLabels: Record<EmbedPlayerSource, string> = { 49 youtube: 'YouTube', ··· 56 appleMusic: 'Apple Music', 57 soundcloud: 'SoundCloud', 58 flickr: 'Flickr', 59 } 60 61 export interface EmbedPlayerParams { ··· 459 return undefined 460 } 461 } 462 } 463 464 export function getPlayerAspect({ ··· 498 return {height: 165} 499 case 'apple_music_song': 500 return {height: 150} 501 default: 502 return {aspectRatio: 16 / 9} 503 }
··· 24 'giphy', 25 'tenor', 26 'flickr', 27 + 'bandcamp', 28 ] as const 29 30 export type EmbedPlayerSource = (typeof embedPlayerSources)[number] ··· 45 | 'giphy_gif' 46 | 'tenor_gif' 47 | 'flickr_album' 48 + | 'bandcamp_album' 49 + | 'bandcamp_track' 50 51 export const externalEmbedLabels: Record<EmbedPlayerSource, string> = { 52 youtube: 'YouTube', ··· 59 appleMusic: 'Apple Music', 60 soundcloud: 'SoundCloud', 61 flickr: 'Flickr', 62 + bandcamp: 'Bandcamp', 63 } 64 65 export interface EmbedPlayerParams { ··· 463 return undefined 464 } 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 + } 492 } 493 494 export function getPlayerAspect({ ··· 528 return {height: 165} 529 case 'apple_music_song': 530 return {height: 150} 531 + case 'bandcamp_album': 532 + case 'bandcamp_track': 533 + return {aspectRatio: 1} 534 default: 535 return {aspectRatio: 16 / 9} 536 }
+1
src/state/persisted/schema.ts
··· 106 appleMusic: z.enum(externalEmbedOptions).optional(), 107 soundcloud: z.enum(externalEmbedOptions).optional(), 108 flickr: z.enum(externalEmbedOptions).optional(), 109 }) 110 .optional(), 111 invites: z.object({
··· 106 appleMusic: z.enum(externalEmbedOptions).optional(), 107 soundcloud: z.enum(externalEmbedOptions).optional(), 108 flickr: z.enum(externalEmbedOptions).optional(), 109 + bandcamp: z.enum(externalEmbedOptions).optional(), 110 }) 111 .optional(), 112 invites: z.object({