From edff1992b18a62f4ada09ba4f20cd2c29cd4e634 Mon Sep 17 00:00:00 2001 From: Ansh Date: Mon, 3 Jul 2023 11:17:12 -0700 Subject: [PATCH] Add permission checks before saving image (#945) * catch permission errors when saving image to album * Save photos to media library alone (not an album) --------- Co-authored-by: Paul Frazee --- src/lib/media/manip.ts | 18 +++----------- src/view/com/lightbox/Lightbox.tsx | 39 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts index 2f6c25e8..a681627e 100644 --- a/src/lib/media/manip.ts +++ b/src/lib/media/manip.ts @@ -108,13 +108,7 @@ export async function shareImageModal({uri}: {uri: string}) { RNFS.unlink(imagePath) } -export async function saveImageToAlbum({ - uri, - album, -}: { - uri: string - album: string -}) { +export async function saveImageToMediaLibrary({uri}: {uri: string}) { // download the file to cache // NOTE // assuming PNG @@ -126,14 +120,8 @@ export async function saveImageToAlbum({ let imagePath = downloadResponse.path() imagePath = normalizePath(await moveToPermanentPath(imagePath, '.png'), true) - // save to the album (creating as needed) - const assetRef = await MediaLibrary.createAssetAsync(imagePath) - const albumRef = await MediaLibrary.getAlbumAsync(album) - if (albumRef) { - await MediaLibrary.addAssetsToAlbumAsync(assetRef, albumRef) - } else { - await MediaLibrary.createAlbumAsync(album, assetRef) - } + // save + await MediaLibrary.createAssetAsync(imagePath) } export function getImageDim(path: string): Promise { diff --git a/src/view/com/lightbox/Lightbox.tsx b/src/view/com/lightbox/Lightbox.tsx index d1fd701c..b496e0d9 100644 --- a/src/view/com/lightbox/Lightbox.tsx +++ b/src/view/com/lightbox/Lightbox.tsx @@ -5,21 +5,47 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import ImageView from './ImageViewing' import {useStores} from 'state/index' import * as models from 'state/models/ui/shell' -import {shareImageModal, saveImageToAlbum} from 'lib/media/manip' +import {shareImageModal, saveImageToMediaLibrary} from 'lib/media/manip' import * as Toast from '../util/Toast' import {Text} from '../util/text/Text' import {s, colors} from 'lib/styles' import {Button} from '../util/forms/Button' import {isIOS} from 'platform/detection' +import * as MediaLibrary from 'expo-media-library' export const Lightbox = observer(function Lightbox() { const store = useStores() const [isAltExpanded, setAltExpanded] = React.useState(false) + const [permissionResponse, requestPermission] = MediaLibrary.usePermissions() const onClose = React.useCallback(() => { store.shell.closeLightbox() }, [store]) + const saveImageToAlbumWithToasts = React.useCallback( + async (uri: string) => { + if (!permissionResponse || permissionResponse.granted === false) { + Toast.show('Permission to access camera roll is required.') + if (permissionResponse?.canAskAgain) { + requestPermission() + } else { + Toast.show( + 'Permission to access camera roll was denied. Please enable it in your system settings.', + ) + } + return + } + + try { + await saveImageToMediaLibrary({uri}) + Toast.show('Saved to your camera roll.') + } catch (e: any) { + Toast.show(`Failed to save image: ${String(e)}`) + } + }, + [permissionResponse, requestPermission], + ) + const LightboxFooter = React.useCallback( ({imageIndex}: {imageIndex: number}) => { const lightbox = store.shell.activeLightbox @@ -74,7 +100,7 @@ export const Lightbox = observer(function Lightbox() { ) }, - [store.shell.activeLightbox, isAltExpanded, setAltExpanded], + [store.shell.activeLightbox, isAltExpanded, saveImageToAlbumWithToasts], ) if (!store.shell.activeLightbox) { @@ -106,15 +132,6 @@ export const Lightbox = observer(function Lightbox() { } }) -async function saveImageToAlbumWithToasts(uri: string) { - try { - await saveImageToAlbum({uri, album: 'Bluesky'}) - Toast.show('Saved to the "Bluesky" album.') - } catch (e: any) { - Toast.show(`Failed to save image: ${String(e)}`) - } -} - const styles = StyleSheet.create({ footer: { paddingTop: 16,