1import MaterialIcons from '@expo/vector-icons/build/MaterialIcons'; 2import * as FileSystem from 'expo-file-system'; 3import * as MediaLibrary from 'expo-media-library'; 4import React from 'react'; 5import { 6 ScrollView, 7 StyleSheet, 8 Text, 9 TouchableOpacity, 10 TouchableOpacityProps, 11 View, 12 Platform, 13} from 'react-native'; 14 15import Photo from './Photo'; 16 17const PHOTOS_DIR = FileSystem.documentDirectory + 'photos'; 18 19interface State { 20 selected: string[]; 21} 22 23function useLoadedPhotos() { 24 const [photos, setPhotos] = React.useState<string[]>([]); 25 26 React.useEffect(() => { 27 let isMounted = true; 28 if (Platform.OS !== 'web') { 29 FileSystem.readDirectoryAsync(PHOTOS_DIR).then((photos) => { 30 if (isMounted) { 31 setPhotos(photos); 32 } 33 }); 34 } 35 return () => { 36 isMounted = false; 37 }; 38 }, []); 39 return photos; 40} 41 42export default function GalleryScreen(props: TouchableOpacityProps & { photos?: string[] }) { 43 const photos = useLoadedPhotos(); 44 return <LoadedGalleryScreen {...props} photos={photos.length ? photos : props.photos ?? []} />; 45} 46 47class LoadedGalleryScreen extends React.Component< 48 TouchableOpacityProps & { photos: string[] }, 49 State 50> { 51 readonly state: State = { 52 selected: [], 53 }; 54 55 toggleSelection = (uri: string, isSelected: boolean) => { 56 this.setState(({ selected }) => { 57 if (isSelected) { 58 selected.push(uri); 59 } else { 60 selected = selected.filter((item) => item !== uri); 61 } 62 return { selected }; 63 }); 64 }; 65 66 saveToGallery = async () => { 67 const photos = this.state.selected; 68 69 if (photos.length > 0) { 70 const { status } = await MediaLibrary.requestPermissionsAsync(); 71 72 if (status !== 'granted') { 73 throw new Error('User has denied the MediaLibrary permissions!'); 74 } 75 76 const promises = photos.map((photoUri) => { 77 return MediaLibrary.createAssetAsync(photoUri); 78 }); 79 80 await Promise.all(promises); 81 alert("Successfully saved photos to user's gallery!"); 82 } else { 83 alert('No photos to save!'); 84 } 85 }; 86 87 renderPhoto = (fileName: string) => ( 88 <Photo 89 key={fileName} 90 uri={Platform.select({ web: fileName, default: `${PHOTOS_DIR}/${fileName}` })} 91 onSelectionToggle={this.toggleSelection} 92 /> 93 ); 94 95 render() { 96 return ( 97 <View style={styles.container}> 98 <View style={styles.navbar}> 99 <TouchableOpacity style={styles.button} onPress={this.props.onPress}> 100 <MaterialIcons name="arrow-back" size={25} color="white" /> 101 </TouchableOpacity> 102 <TouchableOpacity style={styles.button} onPress={this.saveToGallery}> 103 <Text style={styles.whiteText}>Save selected to gallery</Text> 104 </TouchableOpacity> 105 </View> 106 <ScrollView> 107 <View style={styles.pictures}>{this.props.photos.map(this.renderPhoto)}</View> 108 </ScrollView> 109 </View> 110 ); 111 } 112} 113 114const styles = StyleSheet.create({ 115 container: { 116 flex: 1, 117 backgroundColor: 'white', 118 }, 119 navbar: { 120 flexDirection: 'row', 121 alignItems: 'center', 122 justifyContent: 'space-between', 123 backgroundColor: '#4630EB', 124 }, 125 pictures: { 126 flex: 1, 127 flexWrap: 'wrap', 128 flexDirection: 'row', 129 justifyContent: 'space-around', 130 paddingVertical: 8, 131 }, 132 button: { 133 padding: 20, 134 }, 135 whiteText: { 136 color: 'white', 137 }, 138}); 139