From f089f4578131e83cd177b7809ce0f7b75779dfdc Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 21 Jun 2024 21:38:04 -0700 Subject: Starter Packs (#4332) Co-authored-by: Dan Abramov Co-authored-by: Paul Frazee Co-authored-by: Eric Bailey Co-authored-by: Samuel Newman --- src/components/StarterPack/QrCodeDialog.tsx | 201 ++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 src/components/StarterPack/QrCodeDialog.tsx (limited to 'src/components/StarterPack/QrCodeDialog.tsx') diff --git a/src/components/StarterPack/QrCodeDialog.tsx b/src/components/StarterPack/QrCodeDialog.tsx new file mode 100644 index 000000000..580c6cc7c --- /dev/null +++ b/src/components/StarterPack/QrCodeDialog.tsx @@ -0,0 +1,201 @@ +import React from 'react' +import {View} from 'react-native' +import ViewShot from 'react-native-view-shot' +import * as FS from 'expo-file-system' +import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker' +import * as Sharing from 'expo-sharing' +import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {nanoid} from 'nanoid/non-secure' + +import {logger} from '#/logger' +import {saveImageToMediaLibrary} from 'lib/media/manip' +import {logEvent} from 'lib/statsig/statsig' +import {isNative, isWeb} from 'platform/detection' +import * as Toast from '#/view/com/util/Toast' +import {atoms as a} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {DialogControlProps} from '#/components/Dialog' +import {Loader} from '#/components/Loader' +import {QrCode} from '#/components/StarterPack/QrCode' + +export function QrCodeDialog({ + starterPack, + link, + control, +}: { + starterPack: AppBskyGraphDefs.StarterPackView + link?: string + control: DialogControlProps +}) { + const {_} = useLingui() + const [isProcessing, setIsProcessing] = React.useState(false) + + const ref = React.useRef(null) + + const getCanvas = (base64: string): Promise => { + return new Promise(resolve => { + const image = new Image() + image.onload = () => { + const canvas = document.createElement('canvas') + canvas.width = image.width + canvas.height = image.height + + const ctx = canvas.getContext('2d') + ctx?.drawImage(image, 0, 0) + resolve(canvas) + } + image.src = base64 + }) + } + + const onSavePress = async () => { + ref.current?.capture?.().then(async (uri: string) => { + if (isNative) { + const res = await requestMediaLibraryPermissionsAsync() + + if (!res) { + Toast.show( + _( + msg`You must grant access to your photo library to save a QR code`, + ), + ) + return + } + + const filename = `${FS.documentDirectory}/${nanoid(12)}.png` + + // Incase of a FS failure, don't crash the app + try { + await FS.copyAsync({from: uri, to: filename}) + await saveImageToMediaLibrary({uri: filename}) + await FS.deleteAsync(filename) + } catch (e: unknown) { + Toast.show(_(msg`An error occurred while saving the QR code!`)) + logger.error('Failed to save QR code', {error: e}) + return + } + } else { + setIsProcessing(true) + + if (!AppBskyGraphStarterpack.isRecord(starterPack.record)) { + return + } + + const canvas = await getCanvas(uri) + const imgHref = canvas + .toDataURL('image/png') + .replace('image/png', 'image/octet-stream') + + const link = document.createElement('a') + link.setAttribute( + 'download', + `${starterPack.record.name.replaceAll(' ', '_')}_Share_Card.png`, + ) + link.setAttribute('href', imgHref) + link.click() + } + + logEvent('starterPack:share', { + starterPack: starterPack.uri, + shareType: 'qrcode', + qrShareType: 'save', + }) + setIsProcessing(false) + Toast.show( + isWeb + ? _(msg`QR code has been downloaded!`) + : _(msg`QR code saved to your camera roll!`), + ) + control.close() + }) + } + + const onCopyPress = async () => { + setIsProcessing(true) + ref.current?.capture?.().then(async (uri: string) => { + const canvas = await getCanvas(uri) + // @ts-expect-error web only + canvas.toBlob((blob: Blob) => { + const item = new ClipboardItem({'image/png': blob}) + navigator.clipboard.write([item]) + }) + + logEvent('starterPack:share', { + starterPack: starterPack.uri, + shareType: 'qrcode', + qrShareType: 'copy', + }) + Toast.show(_(msg`QR code copied to your clipboard!`)) + setIsProcessing(false) + control.close() + }) + } + + const onSharePress = async () => { + ref.current?.capture?.().then(async (uri: string) => { + control.close(() => { + Sharing.shareAsync(uri, {mimeType: 'image/png', UTI: 'image/png'}).then( + () => { + logEvent('starterPack:share', { + starterPack: starterPack.uri, + shareType: 'qrcode', + qrShareType: 'share', + }) + }, + ) + }) + }) + } + + return ( + + + + + {!link ? ( + + + + ) : ( + <> + + {isProcessing ? ( + + + + ) : ( + + + + + )} + + )} + + + + ) +} -- cgit 1.4.1