mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {View} from 'react-native'
3import type ViewShot from 'react-native-view-shot'
4import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
5import {createAssetAsync} from 'expo-media-library'
6import * as Sharing from 'expo-sharing'
7import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
8import {msg, Trans} from '@lingui/macro'
9import {useLingui} from '@lingui/react'
10
11import {logEvent} from '#/lib/statsig/statsig'
12import {logger} from '#/logger'
13import {isNative, isWeb} from '#/platform/detection'
14import * as Toast from '#/view/com/util/Toast'
15import {atoms as a} from '#/alf'
16import {Button, ButtonText} from '#/components/Button'
17import * as Dialog from '#/components/Dialog'
18import {DialogControlProps} from '#/components/Dialog'
19import {Loader} from '#/components/Loader'
20import {QrCode} from '#/components/StarterPack/QrCode'
21
22export function QrCodeDialog({
23 starterPack,
24 link,
25 control,
26}: {
27 starterPack: AppBskyGraphDefs.StarterPackView
28 link?: string
29 control: DialogControlProps
30}) {
31 const {_} = useLingui()
32 const [isProcessing, setIsProcessing] = React.useState(false)
33
34 const ref = React.useRef<ViewShot>(null)
35
36 const getCanvas = (base64: string): Promise<HTMLCanvasElement> => {
37 return new Promise(resolve => {
38 const image = new Image()
39 image.onload = () => {
40 const canvas = document.createElement('canvas')
41 canvas.width = image.width
42 canvas.height = image.height
43
44 const ctx = canvas.getContext('2d')
45 ctx?.drawImage(image, 0, 0)
46 resolve(canvas)
47 }
48 image.src = base64
49 })
50 }
51
52 const onSavePress = async () => {
53 ref.current?.capture?.().then(async (uri: string) => {
54 if (isNative) {
55 const res = await requestMediaLibraryPermissionsAsync()
56
57 if (!res) {
58 Toast.show(
59 _(
60 msg`You must grant access to your photo library to save a QR code`,
61 ),
62 )
63 return
64 }
65
66 // Incase of a FS failure, don't crash the app
67 try {
68 await createAssetAsync(`file://${uri}`)
69 } catch (e: unknown) {
70 Toast.show(
71 _(msg`An error occurred while saving the QR code!`),
72 'xmark',
73 )
74 logger.error('Failed to save QR code', {error: e})
75 return
76 }
77 } else {
78 setIsProcessing(true)
79
80 if (!AppBskyGraphStarterpack.isRecord(starterPack.record)) {
81 return
82 }
83
84 const canvas = await getCanvas(uri)
85 const imgHref = canvas
86 .toDataURL('image/png')
87 .replace('image/png', 'image/octet-stream')
88
89 const link = document.createElement('a')
90 link.setAttribute(
91 'download',
92 `${starterPack.record.name.replaceAll(' ', '_')}_Share_Card.png`,
93 )
94 link.setAttribute('href', imgHref)
95 link.click()
96 }
97
98 logEvent('starterPack:share', {
99 starterPack: starterPack.uri,
100 shareType: 'qrcode',
101 qrShareType: 'save',
102 })
103 setIsProcessing(false)
104 Toast.show(
105 isWeb
106 ? _(msg`QR code has been downloaded!`)
107 : _(msg`QR code saved to your camera roll!`),
108 )
109 control.close()
110 })
111 }
112
113 const onCopyPress = async () => {
114 setIsProcessing(true)
115 ref.current?.capture?.().then(async (uri: string) => {
116 const canvas = await getCanvas(uri)
117 // @ts-expect-error web only
118 canvas.toBlob((blob: Blob) => {
119 const item = new ClipboardItem({'image/png': blob})
120 navigator.clipboard.write([item])
121 })
122
123 logEvent('starterPack:share', {
124 starterPack: starterPack.uri,
125 shareType: 'qrcode',
126 qrShareType: 'copy',
127 })
128 Toast.show(_(msg`QR code copied to your clipboard!`))
129 setIsProcessing(false)
130 control.close()
131 })
132 }
133
134 const onSharePress = async () => {
135 ref.current?.capture?.().then(async (uri: string) => {
136 control.close(() => {
137 Sharing.shareAsync(uri, {mimeType: 'image/png', UTI: 'image/png'}).then(
138 () => {
139 logEvent('starterPack:share', {
140 starterPack: starterPack.uri,
141 shareType: 'qrcode',
142 qrShareType: 'share',
143 })
144 },
145 )
146 })
147 })
148 }
149
150 return (
151 <Dialog.Outer control={control}>
152 <Dialog.ScrollableInner
153 label={_(msg`Create a QR code for a starter pack`)}>
154 <View style={[a.flex_1, a.align_center, a.gap_5xl]}>
155 <React.Suspense fallback={<Loading />}>
156 {!link ? (
157 <Loading />
158 ) : (
159 <>
160 <QrCode starterPack={starterPack} link={link} ref={ref} />
161 {isProcessing ? (
162 <View>
163 <Loader size="xl" />
164 </View>
165 ) : (
166 <View
167 style={[a.w_full, a.gap_md, isWeb && [a.flex_row_reverse]]}>
168 <Button
169 label={_(msg`Copy QR code`)}
170 variant="solid"
171 color="secondary"
172 size="small"
173 onPress={isWeb ? onCopyPress : onSharePress}>
174 <ButtonText>
175 {isWeb ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
176 </ButtonText>
177 </Button>
178 <Button
179 label={_(msg`Save QR code`)}
180 variant="solid"
181 color="secondary"
182 size="small"
183 onPress={onSavePress}>
184 <ButtonText>
185 <Trans>Save</Trans>
186 </ButtonText>
187 </Button>
188 </View>
189 )}
190 </>
191 )}
192 </React.Suspense>
193 </View>
194 </Dialog.ScrollableInner>
195 </Dialog.Outer>
196 )
197}
198
199function Loading() {
200 return (
201 <View style={[a.align_center, a.p_xl]}>
202 <Loader size="xl" />
203 </View>
204 )
205}