1import { StackNavigationProp } from '@react-navigation/stack';
2import * as MediaLibrary from 'expo-media-library';
3import React from 'react';
4import {
5  FlatList,
6  ListRenderItem,
7  Platform,
8  StyleSheet,
9  Switch,
10  Text,
11  TouchableOpacity,
12  View,
13} from 'react-native';
14
15import MonoText from '../../components/MonoText';
16
17interface State {
18  includeSmartAlbums: boolean;
19  albums: MediaLibrary.Album[];
20}
21
22type Props = {
23  navigation: StackNavigationProp<{ MediaLibrary: { album: MediaLibrary.Album } }>;
24};
25
26export default class MediaAlbumsScreen extends React.Component<Props, State> {
27  static navigationOptions = {
28    title: 'MediaLibrary Albums',
29  };
30
31  state: State = {
32    includeSmartAlbums: false,
33    albums: [],
34  };
35
36  componentDidMount() {
37    this.fetchAlbums(this.state.includeSmartAlbums).then((albums) => this.setState({ albums }));
38  }
39
40  componentDidUpdate(_: Props, lastState: State) {
41    if (lastState.includeSmartAlbums !== this.state.includeSmartAlbums) {
42      this.fetchAlbums(this.state.includeSmartAlbums).then((albums) => this.setState({ albums }));
43    }
44  }
45
46  async fetchAlbums(includeSmartAlbums: boolean) {
47    try {
48      return await MediaLibrary.getAlbumsAsync({
49        includeSmartAlbums,
50      });
51    } catch (e) {
52      if (e.code === 'ERR_NO_ENOUGH_PERMISSIONS') {
53        return [];
54      } else {
55        throw e;
56      }
57    }
58  }
59
60  keyExtractor = (item: MediaLibrary.Album) => item.id;
61
62  openAlbum = (album: MediaLibrary.Album) => {
63    this.props.navigation.navigate('MediaLibrary', { album });
64  };
65
66  renderItem: ListRenderItem<MediaLibrary.Album> = ({ item }) => {
67    return (
68      <TouchableOpacity style={styles.album} onPress={() => this.openAlbum(item)}>
69        <View style={styles.albumHeader}>
70          <Text>{item.title}</Text>
71          <Text>{item.assetCount}</Text>
72        </View>
73        <MonoText>{JSON.stringify(item, null, 2)}</MonoText>
74      </TouchableOpacity>
75    );
76  };
77
78  renderContent() {
79    const { albums } = this.state;
80
81    if (albums.length === 0) {
82      return (
83        <View style={styles.noAlbums}>
84          <Text>
85            You don't have any media albums! You can create one from asset details screen.
86          </Text>
87        </View>
88      );
89    }
90
91    return (
92      <FlatList
93        data={albums}
94        numColumns={1}
95        keyExtractor={this.keyExtractor}
96        renderItem={this.renderItem}
97      />
98    );
99  }
100
101  renderSmartAlbumsToggle() {
102    return (
103      <View style={styles.includeSmartAlbumsRow}>
104        <Text style={styles.includeSmartAlbumsTitle}>Include smart albums</Text>
105        <Switch
106          value={this.state.includeSmartAlbums}
107          onValueChange={() =>
108            this.setState((state) => ({ includeSmartAlbums: !state.includeSmartAlbums }))
109          }
110        />
111      </View>
112    );
113  }
114
115  render() {
116    return (
117      <View style={styles.fill}>
118        {Platform.OS === 'ios' && this.renderSmartAlbumsToggle()}
119        {this.renderContent()}
120      </View>
121    );
122  }
123}
124
125const styles = StyleSheet.create({
126  fill: {
127    flex: 1,
128  },
129  includeSmartAlbumsRow: {
130    flexDirection: 'row',
131    alignItems: 'center',
132    paddingVertical: 6,
133    paddingHorizontal: 10,
134    borderBottomWidth: 1,
135    borderBottomColor: '#cccccc',
136  },
137  includeSmartAlbumsTitle: { flex: 1, fontSize: 16 },
138  album: {
139    flex: 1,
140    paddingVertical: 5,
141    paddingHorizontal: 10,
142    borderBottomWidth: StyleSheet.hairlineWidth,
143    borderBottomColor: 'gray',
144  },
145  albumHeader: {
146    flexDirection: 'row',
147    alignItems: 'center',
148    justifyContent: 'space-between',
149  },
150  noAlbums: {
151    flex: 1,
152    padding: 10,
153    alignItems: 'center',
154    justifyContent: 'center',
155  },
156});
157