diff --git a/src/components/StarterPack/QrCode.tsx b/src/components/StarterPack/QrCode.tsx index 97a2cd3a13..abfd5e8cf0 100644 --- a/src/components/StarterPack/QrCode.tsx +++ b/src/components/StarterPack/QrCode.tsx @@ -31,7 +31,7 @@ export const QrCode = React.forwardRef(function QrCode( (function QrCode( {record.name} - - Join the conversation - - - - - - + - on + Join the conversation - - + + + + + + + on + + + + + + diff --git a/src/components/StarterPack/QrCodeDialog.tsx b/src/components/StarterPack/QrCodeDialog.tsx index 81b9f1f419..82da2a8e9e 100644 --- a/src/components/StarterPack/QrCodeDialog.tsx +++ b/src/components/StarterPack/QrCodeDialog.tsx @@ -1,14 +1,16 @@ import React from 'react' import {View} from 'react-native' import ViewShot from 'react-native-view-shot' +import {setImageAsync} from 'expo-clipboard' import * as FS from 'expo-file-system' import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker' -import {AppBskyGraphDefs} from '@atproto/api' +import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {nanoid} from 'nanoid/non-secure' import {saveImageToMediaLibrary} from 'lib/media/manip' +import {isNative} from 'platform/detection' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' @@ -28,25 +30,79 @@ export function QrCodeDialog({ const ref = React.useRef(null) - const onSavePress = () => { - ref.current?.capture?.().then(async (uri: string) => { - const res = await requestMediaLibraryPermissionsAsync() + 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 - if (!res) { - Toast.show( - _(msg`You must grant access to your photo library to save a QR code`), + 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.cacheDirectory}/${nanoid(12)}.png` + await FS.copyAsync({from: uri, to: filename}) + + await saveImageToMediaLibrary({uri: filename}) + await FS.deleteAsync(filename) + + control.close() + } else { + 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`, ) - return + link.setAttribute('href', imgHref) + link.click() } - const filename = `${FS.cacheDirectory}/${nanoid(12)}.png` - await FS.copyAsync({from: uri, to: filename}) - - await saveImageToMediaLibrary({uri: filename}) - await FS.deleteAsync(filename) Toast.show(_(msg`QR code saved to your camera roll!`)) + }) + } - control.close() + const onCopyPress = async () => { + ref.current?.capture?.().then(async (uri: string) => { + if (isNative) { + await setImageAsync(uri) + } else { + const canvas = await getCanvas(uri) + canvas.toBlob((blob: Blob) => { + const item = new ClipboardItem({'image/png': blob}) + navigator.clipboard.write([item]) + }) + } + + Toast.show(_(msg`QR code copied to your clipboard!`)) }) } @@ -55,21 +111,33 @@ export function QrCodeDialog({ - + Share this starter pack with friends! - + + + + diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx index 254ab4a0cc..0c6e2dd409 100644 --- a/src/screens/StarterPack/Wizard/index.tsx +++ b/src/screens/StarterPack/Wizard/index.tsx @@ -641,7 +641,7 @@ function Footer({ )} )} - {state.currentStep === 'Profiles' && items.length < 8 ? ( + {state.currentStep === 'Profiles' && items.length < 8 && !__DEV__ ? (