feat: Implemented listing
This commit is contained in:
parent
7bf0d428b8
commit
0d1df64c7f
@ -10,14 +10,13 @@ import useLoggedIn from './hooks/useLoggedIn';
|
|||||||
import SignUp from './screens/SignUp';
|
import SignUp from './screens/SignUp';
|
||||||
import Buckets from './screens/Buckets';
|
import Buckets from './screens/Buckets';
|
||||||
import Settings from './screens/Settings';
|
import Settings from './screens/Settings';
|
||||||
|
import DirectoryDetails from './screens/DirectoryDetails';
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator();
|
const Stack = createNativeStackNavigator();
|
||||||
|
|
||||||
const Navigation = () => {
|
const Navigation = () => {
|
||||||
const [isInitializing, user] = useLoggedIn();
|
const [isInitializing, user] = useLoggedIn();
|
||||||
|
|
||||||
console.log('[user]', user);
|
|
||||||
|
|
||||||
if (isInitializing) {
|
if (isInitializing) {
|
||||||
return <Text>Loading...</Text>;
|
return <Text>Loading...</Text>;
|
||||||
}
|
}
|
||||||
@ -35,8 +34,6 @@ const Navigation = () => {
|
|||||||
component={SignUp}
|
component={SignUp}
|
||||||
options={{title: 'Sign Up'}}
|
options={{title: 'Sign Up'}}
|
||||||
/>
|
/>
|
||||||
{!!user ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="Buckets"
|
name="Buckets"
|
||||||
component={Buckets}
|
component={Buckets}
|
||||||
@ -49,13 +46,18 @@ const Navigation = () => {
|
|||||||
),
|
),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="DirectoryDetails"
|
||||||
|
component={DirectoryDetails}
|
||||||
|
options={({route}) => ({
|
||||||
|
title: route.params.dir,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="Settings"
|
name="Settings"
|
||||||
component={Settings}
|
component={Settings}
|
||||||
options={{title: 'Settings'}}
|
options={{title: 'Settings'}}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
|
||||||
) : null}
|
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
|
41
src/components/Bucket.js
Normal file
41
src/components/Bucket.js
Normal file
@ -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 (
|
||||||
|
<Pressable onPress={onPress} style={styles.root}>
|
||||||
|
<Text style={styles.name}>{bucket.Name}</Text>
|
||||||
|
<Text style={styles.createdAt}>
|
||||||
|
Created at: {bucket.CreationDate.toISOString()}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
39
src/components/Directory.js
Normal file
39
src/components/Directory.js
Normal file
@ -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 (
|
||||||
|
<Pressable onPress={onPress} style={styles.root}>
|
||||||
|
<Text style={styles.name}>
|
||||||
|
{slashPos > 0 ? dir.Prefix.slice(slashPos + 1) : dir.Prefix}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
margin: 4,
|
||||||
|
backgroundColor: '#CCCCCC',
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 12,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Directory;
|
39
src/components/File.js
Normal file
39
src/components/File.js
Normal file
@ -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 (
|
||||||
|
<Pressable onPress={onPress} style={styles.root}>
|
||||||
|
<Text style={styles.name}>
|
||||||
|
{slashPos > 0 ? file.Key.slice(slashPos + 1) : file.Key}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
margin: 4,
|
||||||
|
backgroundColor: '#CCCCCC',
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 12,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default File;
|
34
src/hooks/useS3.js
Normal file
34
src/hooks/useS3.js
Normal file
@ -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;
|
60
src/hooks/useSettings.js
Normal file
60
src/hooks/useSettings.js
Normal file
@ -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;
|
@ -1,10 +1,32 @@
|
|||||||
import React from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import {StyleSheet, View, Text, ScrollView} from 'react-native';
|
import {ScrollView, ToastAndroid} from 'react-native';
|
||||||
|
import useS3 from '../hooks/useS3';
|
||||||
|
import Bucket from '../components/Bucket';
|
||||||
|
|
||||||
const Buckets = () => {
|
const Buckets = () => {
|
||||||
|
const s3 = useS3();
|
||||||
|
const [buckets, setBuckets] = useState([]);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<Text>Signed in!!!</Text>
|
<ScrollView>
|
||||||
)
|
{buckets.map(bucket => (
|
||||||
}
|
<Bucket key={bucket.Name} bucket={bucket} />
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Buckets;
|
export default Buckets;
|
58
src/screens/DirectoryDetails.js
Normal file
58
src/screens/DirectoryDetails.js
Normal file
@ -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 (
|
||||||
|
<ScrollView>
|
||||||
|
{contents.dirs.map(dir => (
|
||||||
|
<Directory
|
||||||
|
key={'dir-' + dir.Prefix}
|
||||||
|
bucket={route.params.bucket}
|
||||||
|
dir={dir}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{contents.files.map(file => (
|
||||||
|
<File
|
||||||
|
key={'file-' + file.Key}
|
||||||
|
bucket={route.params.bucket}
|
||||||
|
file={file}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectoryDetails;
|
@ -3,10 +3,19 @@ import {StyleSheet, View, Text, Button, TextInput} from 'react-native';
|
|||||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
|
||||||
import useLogout from '../hooks/useLogout';
|
import useLogout from '../hooks/useLogout';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
|
import useSettings from '../hooks/useSettings';
|
||||||
|
|
||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const logOut = useLogout();
|
const logOut = useLogout();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
const [
|
||||||
|
isLoaded,
|
||||||
|
save,
|
||||||
|
awsKeyId,
|
||||||
|
setAwsKeyId,
|
||||||
|
awsSecretAccessKey,
|
||||||
|
setAwsSecretAccessKey,
|
||||||
|
] = useSettings();
|
||||||
|
|
||||||
const onLogout = useCallback(() => {
|
const onLogout = useCallback(() => {
|
||||||
logOut().then(() => {
|
logOut().then(() => {
|
||||||
@ -14,9 +23,32 @@ const Settings = () => {
|
|||||||
});
|
});
|
||||||
}, [logOut]);
|
}, [logOut]);
|
||||||
|
|
||||||
|
const onSave = useCallback(() => {
|
||||||
|
save().then(() => navigation.goBack());
|
||||||
|
}, [save, navigation]);
|
||||||
|
|
||||||
|
if (!isLoaded) {
|
||||||
|
return <Text>Loading...</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAwareScrollView style={styles.root}>
|
<KeyboardAwareScrollView style={styles.root}>
|
||||||
|
<Text style={styles.label}>AWS Key ID</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
value={awsKeyId}
|
||||||
|
onChangeText={setAwsKeyId}
|
||||||
|
/>
|
||||||
|
<Text style={styles.label}>AWS Secret Access Key</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
value={awsSecretAccessKey}
|
||||||
|
onChangeText={setAwsSecretAccessKey}
|
||||||
|
/>
|
||||||
<View style={styles.button}>
|
<View style={styles.button}>
|
||||||
|
<Button onPress={onSave} title="Save" />
|
||||||
|
</View>
|
||||||
|
<View style={[styles.button, styles.logout]}>
|
||||||
<Button onPress={onLogout} title="Sign out" />
|
<Button onPress={onLogout} title="Sign out" />
|
||||||
</View>
|
</View>
|
||||||
</KeyboardAwareScrollView>
|
</KeyboardAwareScrollView>
|
||||||
@ -29,9 +61,14 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: '#FEFEFE',
|
backgroundColor: '#FEFEFE',
|
||||||
padding: 10,
|
padding: 10,
|
||||||
},
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#333333',
|
||||||
|
},
|
||||||
input: {
|
input: {
|
||||||
height: 40,
|
height: 40,
|
||||||
margin: 12,
|
margin: 12,
|
||||||
|
marginBottom: 24,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
@ -42,6 +79,9 @@ const styles = StyleSheet.create({
|
|||||||
margin: 12,
|
margin: 12,
|
||||||
marginBottom: 32,
|
marginBottom: 32,
|
||||||
},
|
},
|
||||||
|
logout: {
|
||||||
|
marginTop: 64,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Settings;
|
export default Settings;
|
||||||
|
@ -29,7 +29,6 @@ const SignIn = () => {
|
|||||||
|
|
||||||
logIn(email, pass)
|
logIn(email, pass)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('asdasd');
|
|
||||||
navigation.reset({index: 0, routes: [{name: 'Buckets'}]});
|
navigation.reset({index: 0, routes: [{name: 'Buckets'}]});
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
Loading…
Reference in New Issue
Block a user