diff --git a/package-lock.json b/package-lock.json index 6243219..f78fc38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,13 @@ "@react-navigation/native": "^6.0.10", "@react-navigation/native-stack": "^6.6.2", "aws-sdk": "^2.1166.0", + "pretty-bytes": "^6.0.0", "react": "18.0.0", "react-native": "0.69.1", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-safe-area-context": "^4.3.1", - "react-native-screens": "^3.14.0" + "react-native-screens": "^3.14.0", + "rn-fetch-blob": "^0.12.0" }, "devDependencies": { "@babel/core": "^7.12.9", @@ -5378,6 +5380,11 @@ "node": ">=0.10.0" } }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/base/node_modules/define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", @@ -12586,6 +12593,17 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-bytes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz", + "integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -13260,6 +13278,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rn-fetch-blob": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz", + "integrity": "sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==", + "dependencies": { + "base-64": "0.1.0", + "glob": "7.0.6" + } + }, + "node_modules/rn-fetch-blob/node_modules/glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -19496,6 +19539,11 @@ } } }, + "base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -24923,6 +24971,11 @@ "fast-diff": "^1.1.2" } }, + "pretty-bytes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz", + "integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==" + }, "pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -25450,6 +25503,30 @@ "glob": "^7.1.3" } }, + "rn-fetch-blob": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz", + "integrity": "sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==", + "requires": { + "base-64": "0.1.0", + "glob": "7.0.6" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", diff --git a/package.json b/package.json index 3aa1258..7d4030f 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,13 @@ "@react-navigation/native": "^6.0.10", "@react-navigation/native-stack": "^6.6.2", "aws-sdk": "^2.1166.0", + "pretty-bytes": "^6.0.0", "react": "18.0.0", "react-native": "0.69.1", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-safe-area-context": "^4.3.1", - "react-native-screens": "^3.14.0" + "react-native-screens": "^3.14.0", + "rn-fetch-blob": "^0.12.0" }, "devDependencies": { "@babel/core": "^7.12.9", diff --git a/src/Navigation.js b/src/Navigation.js index 4939b03..b4b1025 100644 --- a/src/Navigation.js +++ b/src/Navigation.js @@ -11,6 +11,7 @@ import SignUp from './screens/SignUp'; import Buckets from './screens/Buckets'; import Settings from './screens/Settings'; import DirectoryDetails from './screens/DirectoryDetails'; +import FileDetails from './screens/FileDetails'; const Stack = createNativeStackNavigator(); @@ -53,6 +54,13 @@ const Navigation = () => { title: route.params.dir, })} /> + ({ + title: route.params.file, + })} + /> { }, (err, data) => { if (err) { - console.log(err); ToastAndroid.show( 'Failed to fetch directory contents', ToastAndroid.SHORT, diff --git a/src/screens/FileDetails.js b/src/screens/FileDetails.js new file mode 100644 index 0000000..bb29484 --- /dev/null +++ b/src/screens/FileDetails.js @@ -0,0 +1,81 @@ +import React, {useState, useEffect} from 'react'; +import { + StyleSheet, + ScrollView, + Text, + Button, + ToastAndroid, + PermissionsAndroid, +} from 'react-native'; +import {useRoute} from '@react-navigation/native'; +import prettyBytes from 'pretty-bytes'; +import useS3 from '../hooks/useS3'; + +const FileDetails = () => { + const route = useRoute(); + const s3 = useS3(); + const [data, setData] = useState(null); + + useEffect(() => { + if (s3 && !data) { + s3.getObject( + { + Bucket: route.params.bucket, + Key: route.params.file, + Range: 'bytes=0-1', + }, + (err, data) => { + if (err) { + ToastAndroid.show('Failed to fetch file info', ToastAndroid.SHORT); + return; + } + + setData(data); + }, + ); + } + }, [s3]); + + if (!data) { + return Loading...; + } + + return ( + + Bucket: {route.params.bucket} + Key: {route.params.file} + + Last modified: {data.LastModified.toISOString()} + + Size: {prettyBytes(data.ContentLength)} + Content type: {data.ContentType} + + ); +}; + +const styles = StyleSheet.create({ + root: { + flex: 1, + padding: 12, + }, + bucket: { + fontSize: 14, + marginBottom: 12, + color: '#000000', + }, + fileName: { + fontSize: 16, + fontWeight: 'bold', + marginBottom: 12, + color: '#000000', + }, + info: { + fontSize: 14, + marginBottom: 12, + }, + download: { + marginTop: 16, + }, +}); + +export default FileDetails;