1import { StackScreenProps } from '@react-navigation/stack'; 2import { Image } from 'expo-image'; 3import * as MediaLibrary from 'expo-media-library'; 4import React from 'react'; 5import { ScrollView, StyleSheet, View, Alert } from 'react-native'; 6 7import Button from '../../components/Button'; 8import HeadingText from '../../components/HeadingText'; 9import MonoText from '../../components/MonoText'; 10 11const EXPO_ALBUM_NAME = 'Expo'; 12 13type Links = { 14 MediaDetails: { asset: MediaLibrary.Asset; onGoBack: () => void; album: MediaLibrary.Album }; 15}; 16 17type Props = StackScreenProps<Links, 'MediaDetails'>; 18 19export default class MediaDetailsScreen extends React.Component<Props> { 20 static navigationOptions = { 21 title: 'MediaLibrary Asset', 22 }; 23 24 state = { 25 details: null, 26 detailsWithoutDownloadingFromNetwork: null, 27 }; 28 29 componentDidMount() { 30 const { asset } = this.props.route.params; 31 MediaLibrary.getAssetInfoAsync(asset, { shouldDownloadFromNetwork: false }).then((details) => { 32 this.setState({ detailsWithoutDownloadingFromNetwork: details }); 33 }); 34 MediaLibrary.getAssetInfoAsync(asset).then((details) => { 35 this.setState({ details }); 36 }); 37 } 38 39 goBack() { 40 const { navigation, route } = this.props; 41 const { onGoBack } = route.params; 42 43 if (onGoBack) { 44 onGoBack(); 45 } 46 navigation.goBack(); 47 } 48 49 deleteAsset = async () => { 50 const { asset } = this.props.route.params!; 51 52 await MediaLibrary.deleteAssetsAsync([asset]); 53 this.goBack(); 54 }; 55 56 addToAlbum = async () => { 57 const permissions = await MediaLibrary.getPermissionsAsync(); 58 if (permissions?.accessPrivileges && permissions.accessPrivileges !== 'all') { 59 Alert.alert('Access to all photos is required to do this operation'); 60 return; 61 } 62 63 const { asset } = this.props.route.params!; 64 const expoAlbum = await MediaLibrary.getAlbumAsync(EXPO_ALBUM_NAME); 65 66 if (expoAlbum) { 67 await MediaLibrary.addAssetsToAlbumAsync(asset, expoAlbum); 68 } else { 69 await MediaLibrary.createAlbumAsync(EXPO_ALBUM_NAME, asset); 70 } 71 72 alert('Successfully added asset to Expo album!'); 73 }; 74 75 removeFromAlbum = async () => { 76 const { asset, album } = this.props.route.params!; 77 78 if (album) { 79 await MediaLibrary.removeAssetsFromAlbumAsync(asset.id, album.id); 80 this.goBack(); 81 } 82 }; 83 84 renderAsset(asset: MediaLibrary.Asset) { 85 const aspectRatio = asset.height ? asset.width / asset.height : 1; 86 87 switch (asset.mediaType) { 88 case MediaLibrary.MediaType.photo: 89 case MediaLibrary.MediaType.video: // TODO: render Expo.Video component 90 return ( 91 <Image 92 style={[styles.image, { aspectRatio }]} 93 source={{ uri: asset.uri }} 94 contentFit="cover" 95 /> 96 ); 97 default: 98 return null; 99 } 100 } 101 102 render() { 103 const { details, detailsWithoutDownloadingFromNetwork } = this.state; 104 const { asset, album } = this.props.route.params!; 105 106 return ( 107 <ScrollView style={styles.container}> 108 <View style={styles.buttons}> 109 <Button 110 style={styles.button} 111 buttonStyle={{ backgroundColor: 'red' }} 112 title="Delete asset" 113 onPress={this.deleteAsset} 114 /> 115 {album && ( 116 <Button 117 style={styles.button} 118 buttonStyle={{ backgroundColor: 'red' }} 119 title={`Remove from ${album.title} album`} 120 onPress={this.removeFromAlbum} 121 /> 122 )} 123 </View> 124 {(!album || album.title !== EXPO_ALBUM_NAME) && ( 125 <View style={styles.buttons}> 126 <Button style={styles.button} title="Add to Expo album" onPress={this.addToAlbum} /> 127 </View> 128 )} 129 130 <View style={styles.imageContainer}>{this.renderAsset(asset)}</View> 131 132 <HeadingText>Base asset data</HeadingText> 133 <MonoText>{JSON.stringify(asset, null, 2)}</MonoText> 134 135 {details && ( 136 <View style={styles.details}> 137 <HeadingText>MediaLibrary.getAssetInfoAsync(assetId)</HeadingText> 138 <MonoText>{JSON.stringify(details, null, 2)}</MonoText> 139 </View> 140 )} 141 142 {detailsWithoutDownloadingFromNetwork && ( 143 <View style={styles.details}> 144 <HeadingText>{`MediaLibrary.getAssetInfoAsync(assetId, { shouldDownloadFromNetwork: false })`}</HeadingText> 145 <MonoText>{JSON.stringify(detailsWithoutDownloadingFromNetwork, null, 2)}</MonoText> 146 </View> 147 )} 148 </ScrollView> 149 ); 150 } 151} 152 153const styles = StyleSheet.create({ 154 container: { 155 paddingHorizontal: 10, 156 paddingVertical: 16, 157 flex: 1, 158 }, 159 buttons: { 160 marginBottom: 10, 161 flexDirection: 'row', 162 justifyContent: 'center', 163 alignItems: 'center', 164 }, 165 button: { 166 marginHorizontal: 5, 167 }, 168 imageContainer: { 169 width: '100%', 170 paddingVertical: 10, 171 paddingHorizontal: 25, 172 justifyContent: 'center', 173 }, 174 image: { 175 flex: 1, 176 }, 177 details: { 178 marginBottom: 10, 179 }, 180}); 181