forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {lazy, useState} from 'react'
2import {View} from 'react-native'
3// @ts-expect-error missing types
4import QRCode from 'react-native-qrcode-styled'
5import type ViewShot from 'react-native-view-shot'
6import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
7import {Trans} from '@lingui/macro'
8
9import {Logo} from '#/view/icons/Logo'
10import {Logotype} from '#/view/icons/Logotype'
11import {useTheme} from '#/alf'
12import {atoms as a} from '#/alf'
13import {LinearGradientBackground} from '#/components/LinearGradientBackground'
14import {Text} from '#/components/Typography'
15import {IS_WEB} from '#/env'
16import * as bsky from '#/types/bsky'
17
18const LazyViewShot = lazy(
19 // @ts-expect-error dynamic import
20 () => import('react-native-view-shot/src/index'),
21)
22
23export function QrCode({
24 starterPack,
25 link,
26 ref,
27}: {
28 starterPack: AppBskyGraphDefs.StarterPackView
29 link: string
30 ref: React.Ref<ViewShot>
31}) {
32 const {record} = starterPack
33
34 if (
35 !bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
36 record,
37 AppBskyGraphStarterpack.isRecord,
38 )
39 ) {
40 return null
41 }
42
43 return (
44 <LazyViewShot ref={ref}>
45 <LinearGradientBackground
46 style={[
47 {width: 300, minHeight: 390},
48 a.align_center,
49 a.px_sm,
50 a.py_xl,
51 a.rounded_sm,
52 a.justify_between,
53 a.gap_md,
54 ]}>
55 <View style={[a.gap_sm]}>
56 <Text
57 style={[
58 a.font_semi_bold,
59 a.text_3xl,
60 a.text_center,
61 {color: 'white'},
62 ]}>
63 {record.name}
64 </Text>
65 </View>
66 <View style={[a.gap_xl, a.align_center]}>
67 <Text
68 style={[
69 a.font_semi_bold,
70 a.text_center,
71 {color: 'white', fontSize: 18},
72 ]}>
73 <Trans>Join the conversation</Trans>
74 </Text>
75 <View style={[a.rounded_sm, a.overflow_hidden]}>
76 <QrCodeInner link={link} />
77 </View>
78
79 <Text
80 style={[
81 a.flex,
82 a.flex_row,
83 a.align_center,
84 a.font_semi_bold,
85 {color: 'white', fontSize: 18, gap: 6},
86 ]}>
87 <Trans>
88 on
89 <View style={[a.flex_row, a.align_center, {gap: 6}]}>
90 <Logo width={25} fill="white" />
91 <View style={[{marginTop: 3.5}]}>
92 <Logotype width={72} fill="white" />
93 </View>
94 </View>
95 </Trans>
96 </Text>
97 </View>
98 </LinearGradientBackground>
99 </LazyViewShot>
100 )
101}
102
103export function QrCodeInner({link}: {link: string}) {
104 const t = useTheme()
105 const [logoArea, setLogoArea] = useState<{
106 x: number
107 y: number
108 width: number
109 height: number
110 } | null>(null)
111
112 const onLogoAreaChange = (area: {
113 x: number
114 y: number
115 width: number
116 height: number
117 }) => {
118 setLogoArea(area)
119 }
120
121 return (
122 <View style={{position: 'relative'}}>
123 {/* An SVG version of the logo is placed on top of normal `QRCode` `logo` prop, since the PNG fails to load before the export completes on web. */}
124 {IS_WEB && logoArea && (
125 <View
126 style={{
127 position: 'absolute',
128 left: logoArea.x,
129 top: logoArea.y + 1,
130 zIndex: 1,
131 padding: 4,
132 }}>
133 <Logo width={logoArea.width - 14} height={logoArea.height - 14} />
134 </View>
135 )}
136 <QRCode
137 data={link}
138 style={[
139 a.rounded_sm,
140 {height: 225, width: 225, backgroundColor: '#f3f3f3'},
141 ]}
142 pieceSize={IS_WEB ? 8 : 6}
143 padding={20}
144 pieceBorderRadius={IS_WEB ? 4.5 : 3.5}
145 outerEyesOptions={{
146 topLeft: {
147 borderRadius: [12, 12, 0, 12],
148 color: t.palette.primary_500,
149 },
150 topRight: {
151 borderRadius: [12, 12, 12, 0],
152 color: t.palette.primary_500,
153 },
154 bottomLeft: {
155 borderRadius: [12, 0, 12, 12],
156 color: t.palette.primary_500,
157 },
158 }}
159 innerEyesOptions={{borderRadius: 3}}
160 logo={{
161 href: require('../../../assets/logo.png'),
162 ...(IS_WEB && {
163 onChange: onLogoAreaChange,
164 padding: 28,
165 }),
166 ...(!IS_WEB && {
167 padding: 2,
168 scale: 0.95,
169 }),
170 hidePieces: true,
171 }}
172 />
173 </View>
174 )
175}