diff --git a/src/Navigation.js b/src/Navigation.js
index f461c44..4939b03 100644
--- a/src/Navigation.js
+++ b/src/Navigation.js
@@ -10,14 +10,13 @@ import useLoggedIn from './hooks/useLoggedIn';
import SignUp from './screens/SignUp';
import Buckets from './screens/Buckets';
import Settings from './screens/Settings';
+import DirectoryDetails from './screens/DirectoryDetails';
const Stack = createNativeStackNavigator();
const Navigation = () => {
const [isInitializing, user] = useLoggedIn();
- console.log('[user]', user);
-
if (isInitializing) {
return Loading...;
}
@@ -35,27 +34,30 @@ const Navigation = () => {
component={SignUp}
options={{title: 'Sign Up'}}
/>
- {!!user ? (
-
- ({
- title: 'S3 Buckets',
- headerRight: () => (
- navigation.navigate('Settings')}>
- ⚙
-
- ),
- })}
- />
-
-
- ) : null}
+ ({
+ title: 'S3 Buckets',
+ headerRight: () => (
+ navigation.navigate('Settings')}>
+ ⚙
+
+ ),
+ })}
+ />
+ ({
+ title: route.params.dir,
+ })}
+ />
+
);
diff --git a/src/components/Bucket.js b/src/components/Bucket.js
new file mode 100644
index 0000000..e61e7ac
--- /dev/null
+++ b/src/components/Bucket.js
@@ -0,0 +1,41 @@
+import React, {useCallback} from 'react';
+import {StyleSheet, Text, Pressable} from 'react-native';
+import {useNavigation} from '@react-navigation/native';
+
+const Bucket = ({bucket}) => {
+ const navigation = useNavigation();
+
+ const onPress = useCallback(() => {
+ navigation.navigate('DirectoryDetails', {bucket: bucket.Name, dir: '/'});
+ }, [navigation, bucket]);
+
+ return (
+
+ {bucket.Name}
+
+ Created at: {bucket.CreationDate.toISOString()}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ root: {
+ margin: 4,
+ backgroundColor: '#CCCCCC',
+ borderRadius: 5,
+ padding: 12,
+ },
+ name: {
+ fontSize: 16,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ color: '#000000',
+ },
+ createdAt: {
+ fontSize: 12,
+ color: '#444444',
+ },
+});
+
+export default Bucket;
diff --git a/src/components/Directory.js b/src/components/Directory.js
new file mode 100644
index 0000000..32de0c9
--- /dev/null
+++ b/src/components/Directory.js
@@ -0,0 +1,39 @@
+import React, {useCallback} from 'react';
+import {StyleSheet, Text, Pressable} from 'react-native';
+import {useNavigation} from '@react-navigation/native';
+
+const Directory = ({bucket, dir}) => {
+ const navigation = useNavigation();
+
+ const onPress = useCallback(() => {
+ navigation.push('DirectoryDetails', {
+ bucket: bucket,
+ dir: dir.Prefix,
+ });
+ }, [navigation, bucket]);
+
+ const slashPos = dir.Prefix.lastIndexOf('/', dir.Prefix.length - 2);
+
+ return (
+
+
+ {slashPos > 0 ? dir.Prefix.slice(slashPos + 1) : dir.Prefix}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ root: {
+ margin: 4,
+ backgroundColor: '#CCCCCC',
+ borderRadius: 5,
+ padding: 12,
+ },
+ name: {
+ fontSize: 14,
+ color: '#000000',
+ },
+});
+
+export default Directory;
diff --git a/src/components/File.js b/src/components/File.js
new file mode 100644
index 0000000..28bf638
--- /dev/null
+++ b/src/components/File.js
@@ -0,0 +1,39 @@
+import React, {useCallback} from 'react';
+import {StyleSheet, Text, Pressable} from 'react-native';
+import {useNavigation} from '@react-navigation/native';
+
+const File = ({bucket, file}) => {
+ const navigation = useNavigation();
+
+ const onPress = useCallback(() => {
+ navigation.push('FileDetails', {
+ bucket: bucket,
+ file: file.Key,
+ });
+ }, [navigation, bucket]);
+
+ const slashPos = file.Key.lastIndexOf('/', file.Key.length - 2);
+
+ return (
+
+
+ {slashPos > 0 ? file.Key.slice(slashPos + 1) : file.Key}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ root: {
+ margin: 4,
+ backgroundColor: '#CCCCCC',
+ borderRadius: 5,
+ padding: 12,
+ },
+ name: {
+ fontSize: 14,
+ color: '#000000',
+ },
+});
+
+export default File;
diff --git a/src/hooks/useS3.js b/src/hooks/useS3.js
new file mode 100644
index 0000000..d79b341
--- /dev/null
+++ b/src/hooks/useS3.js
@@ -0,0 +1,34 @@
+import {useEffect, useState} from 'react';
+import S3 from 'aws-sdk/clients/s3';
+import useSettings from './useSettings';
+
+const useS3 = () => {
+ const [
+ loaded,
+ _save,
+ awsKeyId,
+ _setAwsKeyId,
+ awsSecretAccessKey,
+ _setAwsSecretAccessKey,
+ ] = useSettings();
+ const [s3, setS3] = useState(null);
+
+ useEffect(() => {
+ if (loaded && !s3) {
+ setS3(
+ new S3({
+ apiVersion: '2006-03-01',
+ region: 'eu-west-1',
+ credentials: {
+ accessKeyId: awsKeyId,
+ secretAccessKey: awsSecretAccessKey,
+ },
+ }),
+ );
+ }
+ }, [loaded]);
+
+ return s3;
+};
+
+export default useS3;
diff --git a/src/hooks/useSettings.js b/src/hooks/useSettings.js
new file mode 100644
index 0000000..0bd4e00
--- /dev/null
+++ b/src/hooks/useSettings.js
@@ -0,0 +1,60 @@
+import {useState, useCallback, useEffect} from 'react';
+import {ToastAndroid} from 'react-native';
+import firestore from '@react-native-firebase/firestore';
+import useLoggedIn from './useLoggedIn';
+
+const useSettings = () => {
+ const [ready, user] = useLoggedIn();
+ const [awsKeyId, setAwsKeyId] = useState('');
+ const [awsSecretAccessKey, setAwsSecretAccessKey] = useState('');
+ const [isLoaded, setIsLoaded] = useState(false);
+
+ useEffect(() => {
+ if (!isLoaded && user) {
+ firestore()
+ .collection('user-settings')
+ .doc(user.uid)
+ .get()
+ .then(doc => {
+ if (doc) {
+ const data = doc.data();
+ setAwsKeyId(data['aws.keyId']);
+ setAwsSecretAccessKey(data['aws.secretAccessKey']);
+ }
+ })
+ .catch(() => {})
+ .finally(() => {
+ setIsLoaded(true);
+ });
+ }
+ }, [user]);
+
+ const save = useCallback(() => {
+ return firestore()
+ .collection('user-settings')
+ .doc(user.uid)
+ .set({
+ 'aws.keyId': awsKeyId,
+ 'aws.secretAccessKey': awsSecretAccessKey,
+ })
+ .then(() => {
+ ToastAndroid.show('Successfully saved settings', ToastAndroid.SHORT);
+ })
+ .catch(e => {
+ console.log(e);
+ ToastAndroid.show('Failed to save settings', ToastAndroid.SHORT);
+ throw e;
+ });
+ }, [user, awsKeyId, awsSecretAccessKey]);
+
+ return [
+ isLoaded,
+ save,
+ awsKeyId,
+ setAwsKeyId,
+ awsSecretAccessKey,
+ setAwsSecretAccessKey,
+ ];
+};
+
+export default useSettings;
diff --git a/src/screens/Buckets.js b/src/screens/Buckets.js
index e89a72f..10ed579 100644
--- a/src/screens/Buckets.js
+++ b/src/screens/Buckets.js
@@ -1,10 +1,32 @@
-import React from 'react';
-import {StyleSheet, View, Text, ScrollView} from 'react-native';
+import React, {useState, useEffect} from 'react';
+import {ScrollView, ToastAndroid} from 'react-native';
+import useS3 from '../hooks/useS3';
+import Bucket from '../components/Bucket';
const Buckets = () => {
- return (
- Signed in!!!
- )
-}
+ const s3 = useS3();
+ const [buckets, setBuckets] = useState([]);
-export default Buckets;
\ No newline at end of file
+ useEffect(() => {
+ if (s3 && !buckets.length) {
+ s3.listBuckets({}, (err, data) => {
+ if (err) {
+ ToastAndroid.show('Failed to fetch buckets', ToastAndroid.SHORT);
+ return;
+ }
+
+ setBuckets(data.Buckets);
+ });
+ }
+ }, [s3]);
+
+ return (
+
+ {buckets.map(bucket => (
+
+ ))}
+
+ );
+};
+
+export default Buckets;
diff --git a/src/screens/DirectoryDetails.js b/src/screens/DirectoryDetails.js
new file mode 100644
index 0000000..a596389
--- /dev/null
+++ b/src/screens/DirectoryDetails.js
@@ -0,0 +1,58 @@
+import React, {useState, useEffect} from 'react';
+import {ScrollView, ToastAndroid} from 'react-native';
+import {useRoute} from '@react-navigation/native';
+import useS3 from '../hooks/useS3';
+import Directory from '../components/Directory';
+import File from '../components/File';
+
+const DirectoryDetails = () => {
+ const route = useRoute();
+ const s3 = useS3();
+ const [contents, setContents] = useState({dirs: [], files: []});
+
+ useEffect(() => {
+ if (s3 && !contents.length) {
+ s3.listObjects(
+ {
+ Bucket: route.params.bucket,
+ Prefix: route.params.dir == '/' ? '' : route.params.dir,
+ Delimiter: '/',
+ MaxKeys: 5000,
+ },
+ (err, data) => {
+ if (err) {
+ console.log(err);
+ ToastAndroid.show(
+ 'Failed to fetch directory contents',
+ ToastAndroid.SHORT,
+ );
+ return;
+ }
+
+ setContents({dirs: data.CommonPrefixes, files: data.Contents});
+ },
+ );
+ }
+ }, [s3]);
+
+ return (
+
+ {contents.dirs.map(dir => (
+
+ ))}
+ {contents.files.map(file => (
+
+ ))}
+
+ );
+};
+
+export default DirectoryDetails;
diff --git a/src/screens/Settings.js b/src/screens/Settings.js
index 690b22f..ee5f542 100644
--- a/src/screens/Settings.js
+++ b/src/screens/Settings.js
@@ -3,10 +3,19 @@ import {StyleSheet, View, Text, Button, TextInput} from 'react-native';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
import useLogout from '../hooks/useLogout';
import {useNavigation} from '@react-navigation/native';
+import useSettings from '../hooks/useSettings';
const Settings = () => {
const logOut = useLogout();
const navigation = useNavigation();
+ const [
+ isLoaded,
+ save,
+ awsKeyId,
+ setAwsKeyId,
+ awsSecretAccessKey,
+ setAwsSecretAccessKey,
+ ] = useSettings();
const onLogout = useCallback(() => {
logOut().then(() => {
@@ -14,9 +23,32 @@ const Settings = () => {
});
}, [logOut]);
+ const onSave = useCallback(() => {
+ save().then(() => navigation.goBack());
+ }, [save, navigation]);
+
+ if (!isLoaded) {
+ return Loading...;
+ }
+
return (
+ AWS Key ID
+
+ AWS Secret Access Key
+
+
+
+
@@ -29,9 +61,14 @@ const styles = StyleSheet.create({
backgroundColor: '#FEFEFE',
padding: 10,
},
+ label: {
+ fontSize: 14,
+ color: '#333333',
+ },
input: {
height: 40,
margin: 12,
+ marginBottom: 24,
borderWidth: 1,
padding: 10,
borderRadius: 3,
@@ -42,6 +79,9 @@ const styles = StyleSheet.create({
margin: 12,
marginBottom: 32,
},
+ logout: {
+ marginTop: 64,
+ },
});
export default Settings;
diff --git a/src/screens/SignIn.js b/src/screens/SignIn.js
index 1900c75..5f8048b 100644
--- a/src/screens/SignIn.js
+++ b/src/screens/SignIn.js
@@ -29,7 +29,6 @@ const SignIn = () => {
logIn(email, pass)
.then(() => {
- console.log('asdasd');
navigation.reset({index: 0, routes: [{name: 'Buckets'}]});
})
.catch(() => {});