mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at utm-source 205 lines 6.1 kB view raw
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}