forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {View} from 'react-native'
2import {msg} from '@lingui/core/macro'
3import {useLingui} from '@lingui/react'
4
5import {
6 usePdsLabelEnabled,
7 usePdsLabelHideBskyPds,
8} from '#/state/preferences/pds-label'
9import {usePdsFaviconQuery, usePdsLabelQuery} from '#/state/queries/pds-label'
10import {atoms as a, useBreakpoints} from '#/alf'
11import {Button} from '#/components/Button'
12import * as Dialog from '#/components/Dialog'
13import {PdsBadgeIcon, PdsDialog} from '#/components/PdsDialog'
14import {IS_WEB} from '#/env'
15
16export function PdsBadge({
17 did,
18 handle,
19 size,
20 interactive = true,
21}: {
22 did: string
23 handle?: string
24 size: 'lg' | 'md' | 'sm'
25 interactive?: boolean
26}) {
27 const enabled = usePdsLabelEnabled()
28 const hideBskyPds = usePdsLabelHideBskyPds()
29 const {data, isLoading} = usePdsLabelQuery(enabled ? did : undefined)
30 const {data: faviconUrl} = usePdsFaviconQuery(
31 data && !data.isBsky && !data.isBridged ? data.pdsUrl : undefined,
32 )
33
34 const isBskyHandle =
35 !!handle && (handle.endsWith('.bsky.social') || handle === 'bsky.social')
36
37 if (!enabled) return null
38 if (isLoading) return <PdsBadgeLoading size={size} isBsky={isBskyHandle} />
39 if (!data) return null
40 if (hideBskyPds && data.isBsky) return null
41
42 return (
43 <PdsBadgeInner
44 pdsUrl={data.pdsUrl}
45 faviconUrl={faviconUrl}
46 isBsky={data.isBsky}
47 isBridged={data.isBridged}
48 size={size}
49 interactive={interactive}
50 />
51 )
52}
53
54function PdsBadgeLoading({
55 size,
56 isBsky = false,
57}: {
58 size: 'lg' | 'md' | 'sm'
59 isBsky?: boolean
60}) {
61 const {gtPhone} = useBreakpoints()
62 let dimensions = 12
63 if (size === 'lg') {
64 dimensions = gtPhone ? 20 : 18
65 } else if (size === 'md') {
66 dimensions = 14
67 }
68 return (
69 <View style={{width: dimensions, height: dimensions}}>
70 <PdsBadgeIcon
71 faviconUrl={undefined}
72 isBsky={isBsky}
73 isBridged={false}
74 size={dimensions}
75 borderRadius={Math.round(dimensions * 0.25)}
76 />
77 </View>
78 )
79}
80
81function PdsBadgeInner({
82 pdsUrl,
83 faviconUrl,
84 isBsky,
85 isBridged,
86 size,
87 interactive,
88}: {
89 pdsUrl: string
90 faviconUrl: string | undefined
91 isBsky: boolean
92 isBridged: boolean
93 size: 'lg' | 'md' | 'sm'
94 interactive: boolean
95}) {
96 const {_} = useLingui()
97 const {gtPhone} = useBreakpoints()
98 const dialogControl = Dialog.useDialogControl()
99
100 let dimensions = 12
101 if (size === 'lg') {
102 dimensions = gtPhone ? 20 : 18
103 } else if (size === 'md') {
104 dimensions = 14
105 }
106
107 const icon = (
108 <PdsBadgeIcon
109 faviconUrl={faviconUrl}
110 isBsky={isBsky}
111 isBridged={isBridged}
112 size={dimensions}
113 borderRadius={Math.round(dimensions * 0.25)}
114 />
115 )
116
117 if (!interactive) {
118 return (
119 <View
120 style={[
121 a.justify_center,
122 a.align_center,
123 {width: dimensions, height: dimensions},
124 ]}>
125 {icon}
126 </View>
127 )
128 }
129
130 return (
131 <>
132 <Button
133 label={_(msg`View PDS information`)}
134 hitSlop={20}
135 onPress={evt => {
136 evt.preventDefault()
137 dialogControl.open()
138 if (IS_WEB) {
139 ;(document.activeElement as HTMLElement | null)?.blur()
140 }
141 }}>
142 {({hovered}) => (
143 <View style={{width: dimensions, height: dimensions}}>
144 <View
145 style={[
146 a.justify_center,
147 a.align_center,
148 a.transition_transform,
149 {
150 position: 'absolute',
151 top: 0,
152 left: 0,
153 right: 0,
154 bottom: 0,
155 transform: [{scale: hovered ? 1.1 : 1}],
156 },
157 ]}>
158 {icon}
159 </View>
160 </View>
161 )}
162 </Button>
163
164 <PdsDialog
165 control={dialogControl}
166 pdsUrl={pdsUrl}
167 faviconUrl={faviconUrl}
168 />
169 </>
170 )
171}