tangled
alpha
login
or
join now
robinwobin.dev
/
witchsky.app
forked from
jollywhoppers.com/witchsky.app
0
fork
atom
Bluesky app fork with some witchin' additions 💫
0
fork
atom
overview
issues
pulls
pipelines
Bandcamp embed (#9445)
authored by
samuel.fm
and committed by
GitHub
4 weeks ago
c83dddce
79542897
+67
-14
4 changed files
expand all
collapse all
unified
split
__tests__
lib
string.test.ts
src
components
dialogs
EmbedConsent.tsx
lib
strings
embed-player.ts
state
persisted
schema.ts
+24
__tests__/lib/string.test.ts
···
437
438
'https://www.flickr.com/groups/898944@N23/',
439
'https://www.flickr.com/groups',
0
0
0
0
0
0
0
440
]
441
442
const outputs = [
···
813
playerUri: 'https://embedr.flickr.com/groups/898944@N23',
814
},
815
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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'
0
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()
0
28
const setExternalEmbedPref = useSetExternalEmbedPref()
0
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
0
79
label={_(msg`Enable external media`)}
80
onPress={onShowAllPress}
81
onAccessibilityEscape={control.close}
82
color="primary"
83
+
size="large">
0
84
<ButtonText>
85
<Trans>Enable external media</Trans>
86
</ButtonText>
87
</Button>
88
<Button
0
89
label={_(msg`Enable this source only`)}
90
onPress={onShowPress}
91
onAccessibilityEscape={control.close}
92
color="secondary"
93
+
size="large">
0
94
<ButtonText>
95
<Trans>Enable {externalEmbedLabels[source]} only</Trans>
96
</ButtonText>
+33
src/lib/strings/embed-player.ts
···
24
'giphy',
25
'tenor',
26
'flickr',
0
27
] as const
28
29
export type EmbedPlayerSource = (typeof embedPlayerSources)[number]
···
44
| 'giphy_gif'
45
| 'tenor_gif'
46
| 'flickr_album'
0
0
47
48
export const externalEmbedLabels: Record<EmbedPlayerSource, string> = {
49
youtube: 'YouTube',
···
56
appleMusic: 'Apple Music',
57
soundcloud: 'SoundCloud',
58
flickr: 'Flickr',
0
59
}
60
61
export interface EmbedPlayerParams {
···
459
return undefined
460
}
461
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
462
}
463
464
export function getPlayerAspect({
···
498
return {height: 165}
499
case 'apple_music_song':
500
return {height: 150}
0
0
0
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(),
0
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({