Use add-only photo permission when saving QR codes (APP-2374)
createAssetAsync fetches the created asset back after writing, which is a photo library read - on a fresh iOS install this triggers the full library permission prompt and rejects, showing a failure toast at the same time. saveToLibraryAsync writes without reading, matching the lightbox image download flow. Also applies to the starter pack QR dialog, which additionally requested the read-scoped media library permission from expo-image-picker.
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
import {Suspense, useRef, useState} from 'react'
|
import {Suspense, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import type ViewShot from 'react-native-view-shot'
|
import type ViewShot from 'react-native-view-shot'
|
||||||
import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
|
import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library'
|
||||||
import {createAssetAsync} from 'expo-media-library'
|
|
||||||
import * as Sharing from 'expo-sharing'
|
import * as Sharing from 'expo-sharing'
|
||||||
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
@@ -60,7 +59,9 @@ export function QrCodeDialog({
|
|||||||
const onSavePress = async () => {
|
const onSavePress = async () => {
|
||||||
ref.current?.capture?.().then(async (uri: string) => {
|
ref.current?.capture?.().then(async (uri: string) => {
|
||||||
if (IS_NATIVE) {
|
if (IS_NATIVE) {
|
||||||
const res = await requestMediaLibraryPermissionsAsync()
|
// Write-only permission - saving the QR image does not require read
|
||||||
|
// access to the user's photo library.
|
||||||
|
const res = await requestPermissionsAsync(true)
|
||||||
|
|
||||||
if (!res.granted) {
|
if (!res.granted) {
|
||||||
Toast.show(
|
Toast.show(
|
||||||
@@ -73,7 +74,9 @@ export function QrCodeDialog({
|
|||||||
|
|
||||||
// Incase of a FS failure, don't crash the app
|
// Incase of a FS failure, don't crash the app
|
||||||
try {
|
try {
|
||||||
await createAssetAsync(`file://${uri}`)
|
// saveToLibraryAsync writes without reading the asset back, so it
|
||||||
|
// works with the add-only permission on iOS (APP-2374)
|
||||||
|
await saveToLibraryAsync(`file://${uri}`)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
Toast.show(_(msg`An error occurred while saving the QR code!`), {
|
Toast.show(_(msg`An error occurred while saving the QR code!`), {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {Suspense, useRef, useState} from 'react'
|
|||||||
import {Pressable, View} from 'react-native'
|
import {Pressable, View} from 'react-native'
|
||||||
import type ViewShot from 'react-native-view-shot'
|
import type ViewShot from 'react-native-view-shot'
|
||||||
import {setStringAsync} from 'expo-clipboard'
|
import {setStringAsync} from 'expo-clipboard'
|
||||||
import {createAssetAsync, requestPermissionsAsync} from 'expo-media-library'
|
import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library'
|
||||||
import {useLingui} from '@lingui/react/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
@@ -92,7 +92,10 @@ export function InviteFriendsDialogInner({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createAssetAsync(`file://${uri}`)
|
// saveToLibraryAsync writes without reading the asset back, so it works
|
||||||
|
// with the add-only permission. createAssetAsync fetches the created
|
||||||
|
// asset, which triggers the full library read prompt on iOS (APP-2374).
|
||||||
|
await saveToLibraryAsync(`file://${uri}`)
|
||||||
ax.metric('invite:action:download', {})
|
ax.metric('invite:action:download', {})
|
||||||
Toast.show(l`QR code saved to your camera roll!`)
|
Toast.show(l`QR code saved to your camera roll!`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user