diff --git a/client/src/components/Home.js b/client/src/components/Home.js index 7f46bf0..44ae940 100644 --- a/client/src/components/Home.js +++ b/client/src/components/Home.js @@ -1,10 +1,12 @@ import Header from "./Header" import SuperheroName from "./SuperheroName" import SendFiles from "./SendFiles" -import Receive from './Receive'; +import "../styles/button.css"; +import "../styles/containers.css"; import WaitForBumpSender from './WaitForBumpSender' +import WaitForBumpReceiver from './WaitForBumpReceiver' import React from 'react' -import { APIsend } from './ApiFetch' +import { APIrecv, APIsend } from './ApiFetch' class Home extends React.Component { constructor(props) { @@ -19,6 +21,12 @@ class Home extends React.Component { } } + onReceiveButtonClick = () => { + this.setState({ + appState: "WaitingToReceive", + }); + } + /** * Called by SendFiles when the user has pressed the "Send" button. * @param link: Magnet link send by SendFiles @@ -48,23 +56,58 @@ class Home extends React.Component { APIsend(clientData); } + + /** + * Called by WaitForBumpReceiver when a BAM/Bump has been detected + * @param sensorData: Coordinates and Date received by WaitForBumpReceiver from db + */ + receiverBumpCallback = (sensorData) => { + console.log("Sender BAM!", sensorData); + + // Build the API request body + const clientData = { + name: "Placeholder", + magnetLink: this.state.magnetLink, + coordinates: sensorData.coordinates, + date: sensorData.date, + } + + APIrecv(clientData); + } render() { return (
- {this.state.appState == "Choosing" &&
} + {(this.state.appState == "Choosing" || this.state.appState == "WaitingToReceive") &&
} {this.state.appState == "Choosing" && } - - + {(this.state.appState == "Choosing" || this.state.appState == "ReadyToSend") && + } + {this.state.appState == "ReadyToSend" && ( )} - - {this.state.appState == "Choosing" && } + + {this.state.appState == "Choosing" && ( + + )} + + {this.state.appState == "WaitingToReceive" && ( + + + )} +
); } diff --git a/client/src/components/Receive.js b/client/src/components/Receive.js index 41de59d..0b09440 100644 --- a/client/src/components/Receive.js +++ b/client/src/components/Receive.js @@ -40,6 +40,11 @@ const Receive = ({ torrent }) => { let torrentId = response.magnetLink; // https://webtorrent.io/docs client.add(torrentId, function (torrent) { + while (torrent.progress != 1) { + console.log('Progress: ' + (torrent.progress * 100).toFixed(1) + '%') + setProgress((torrent.progress * 100).toFixed(1)); + setInterval() + } torrent.files.forEach(function (file) { zip.file(file.path, Blob, { base64: true }); // temporary code. will be replaced with updating Parent Component torr state diff --git a/client/src/components/SendFiles.js b/client/src/components/SendFiles.js index a0cad1f..49f88a0 100644 --- a/client/src/components/SendFiles.js +++ b/client/src/components/SendFiles.js @@ -19,15 +19,20 @@ class SendFiles extends React.Component { // Populates pre component with file names handleFile = (e) => { + // Creating html components + e.preventDefault() const fileListContainer = document.getElementById('fileListContainer') const fileList = document.getElementById('filelist') const userFiles = document.getElementById('files').files - - e.preventDefault() + + // Loop through userFiles and set html data to each file name fileList.innerHTML = '' for (let i = 0; i < userFiles.length; i++) { fileList.innerHTML += userFiles[i].name + '\n\n' } + + // If the list is empty, display nothing + // Else, display the html components if (fileList.innerHTML === '' || fileList.innerHTML === null) { fileList.style.display = 'none' fileListContainer.style.display = 'none' @@ -36,6 +41,7 @@ class SendFiles extends React.Component { fileListContainer.style.display = 'flex' } + // Set the current files state to userFiles this.setState({ files: userFiles }); @@ -45,9 +51,10 @@ class SendFiles extends React.Component { * When the "Send Files" button is clicked, this function is called */ sendButtonClicked = () => { - // FILES TO SEND + // Files to send console.log(this.state.files); - // CREATE TORRENT SEED + // Create torrent seed + console.log(this.state.client) let returnMagnetLink = this.props.pressedSendButtonCallback; this.state.client.seed(this.state.files, function (torrent) { // console.log("Client is seeding:\n" + torrent.magnetURI); @@ -67,7 +74,6 @@ class SendFiles extends React.Component { {/* Makes it so that when you click "Send", it un-renders the "Choose Files" and "Send" Button */} {this.state.isChoosingFiles && (
- {/* File Choosing */} { - const navigate = useNavigate() - return ( -
-
- - -
- Fist Bump Waiting Pic +/* + Waiting for Bump Receiver page + + Receiver renders this component after clicking Receive button on Home + Currently, this component simulates the BAM action and starts receiving files + Later, application should render TransferInProgress while downloading + Then, render ReceiveFiles once done downloading + But for now, this component contains all that functionality + +*/ +class WaitForBumpReceiver extends React.Component { + constructor(props) { + super(props); + this.state = { + client: this.props.client, + showProgress: false + } + } + + // (will be) Called when detected BAM action + bamEvent = () => { + let addedFiles = 0; + var zip = new JSZip(); + const clientData = { + name: "Superman", + // magnetLink: "", + coordinates: [115, 115], + date: Date.now(), + }; + + // Check if the client has been created and created one if false + if(this.state.client == null){ + const { WebTorrent } = window + this.setState({ + client: new WebTorrent() + }); + } + + // Display loading spinner (and progress) + this.setState({ + showProgress: true + }) + + // API fetch call to grab magnet link from matching sender + APIrecv(clientData).then((response) => { + + // Log response + console.log("Torrent ID: ", response.magnetLink); + let torrentId = response.magnetLink; + + // https://webtorrent.io/docs + // Start downloading torrent + this.state.client.add(torrentId, function (torrent) { + + // Loop through each file in the torrent + torrent.files.forEach(function (file) { + + // Set the parameters for the zip file + zip.file(file.path, Blob, { base64: true }); + + // Start zip into blob object for href download + file.getBlob(function (err, blob) { + if (err) throw err; + addedFiles += 1; + + // Add file path to zip + zip.file(file.path, blob); + + // Start the download when all files have been added + if (addedFiles === torrent.files.length) { + + // Name the the file in the zip and start generating each file component + if (torrent.files.length > 1) { zip = zip.folder(torrent.name);} + zip.generateAsync({ type: "blob" }).then(function (blob) { + // Start creating and editing html components + let downloadList = document.getElementById("downloadList"); + let fileRow = document.createElement("div"); + fileRow.className = "downloadAllContainer"; + const url = URL.createObjectURL(blob); + + // Create Download All Files button html component + const a = document.createElement("a"); + a.download = "downloaded_files"; + a.href = url; + a.textContent = "Download All Files"; + a.className = "downloadAllButton"; + + // Add components to html body + fileRow.appendChild(a); + downloadList.appendChild(fileRow); + setTimeout(function () { + URL.revokeObjectURL(url); + }, 30 * 1000); + }); + } + }); + + // Start turning each individual file into blob object + file.getBlobURL(function callback(err, url) { + if (err) throw err; + + // Start creating and editing html components + let downloadList = document.getElementById("downloadList"); + let fileRow = document.createElement("div"); + fileRow.className = "row fileRow"; + downloadList.appendChild(fileRow); + + // Create html component for file names + var p = document.createElement("p"); + p.innerHTML = file.name; + p.className = "col-10 fileNameContainer"; + + // Create html component for individual download button + var a = document.createElement("a"); + a.download = file.name; + a.href = url; + a.className = "col-4 downloadButton"; + + // Insert download image into button + var img = document.createElement("img"); + img.className = "downloadIcon"; + img.src = downloadIcon; + a.appendChild(img); + + // Add components to html body + fileRow.appendChild(p); + fileRow.appendChild(a); + }); + }); + }); + + // Once torrent is done downloading, stop showing loading spinner/progress + }).then(() => { + setTimeout(function () {}, 30 * 1000); + this.setState({ + showProgress: false + }) + }) + } + + cancelReceive = () => { + useNavigate('/'); + } + + render(){ return ( +
+ {/* Fist bump image is the button. Simulates the bump */} + + +


FIST BUMP THE DEVICES
TO INITIATE TRANSFER!

-
- -
- ) +
+ + {/* If currently downloading torrent, render loading spinner; else, render nothing */} + { this.state.showProgress + ?
Loading...
+ : null} + + {/* Container for the downloaded files */} +
+
+ + {/* Cancel button (currently does not work) */} + +
+ )} } +// Declaring prop types +WaitForBumpReceiver.propTypes = { + showProgress: PropTypes.bool, + client: PropTypes.object, + bumpCallback: PropTypes.func +}; + export default WaitForBumpReceiver \ No newline at end of file diff --git a/client/src/components/WaitForBumpSender.js b/client/src/components/WaitForBumpSender.js index 658946f..ec07c58 100644 --- a/client/src/components/WaitForBumpSender.js +++ b/client/src/components/WaitForBumpSender.js @@ -18,25 +18,26 @@ class WaitForBumpSender extends React.Component { } this.props.bumpCallback(sensorData); } + + cancelSend = () => { + useNavigate('/'); + } render(){ return (
- + + +

+
+ FIST BUMP THE DEVICES +
+ TO INITIATE TRANSFER! +

-
- Fist Bump Waiting Pic -

-
- FIST BUMP THE DEVICES -
- TO INITIATE TRANSFER! -

+
- - -
)} } diff --git a/client/src/styles/containers.css b/client/src/styles/containers.css index d5f7d3a..b03804e 100644 --- a/client/src/styles/containers.css +++ b/client/src/styles/containers.css @@ -12,8 +12,21 @@ ul{ align-items: center; border: 1px solid rgb(107, 107, 107); } +.fileList{ + max-height: 200px; + margin: auto; + padding: auto; +} +.fileListContainer{ + min-height: 200px; + max-height: 200px; + margin: auto; + margin-bottom: 1%; + padding: auto; +} .fileNameContainer{ margin: 0; + overflow-wrap: break-word; } .downloadListContainer{ padding: 5%; diff --git a/client/src/styles/transferinprogress.css b/client/src/styles/transferinprogress.css index 745f4e2..842914c 100644 --- a/client/src/styles/transferinprogress.css +++ b/client/src/styles/transferinprogress.css @@ -3,17 +3,98 @@ background-color: black; color: white; text-align: center; - width: 500px; + width: 80%; margin: 0 auto; border-radius: 25px; line-height: 1.5; - padding-bottom: 250px; margin-top: 20px; - } .text-style { font-family: 'Bangers', cursive; font-size: 36px; - margin-top: 50px; + margin-top: none; +} + + +/* credit to lukehaas for loading spinner*/ +.loader { + color: #3b3b3b; + font-size: 90px; + text-indent: -9999em; + overflow: hidden; + width: 1em; + height: 1em; + border-radius: 50%; + margin: 72px auto; + position: relative; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation: load6 1.7s infinite ease, round 1.7s infinite ease; + animation: load6 1.7s infinite ease, round 1.7s infinite ease; +} +@-webkit-keyframes load6 { + 0% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } + 5%, + 95% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } + 10%, + 59% { + box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em; + } + 20% { + box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em; + } + 38% { + box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em; + } + 100% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } +} +@keyframes load6 { + 0% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } + 5%, + 95% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } + 10%, + 59% { + box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em; + } + 20% { + box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em; + } + 38% { + box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em; + } + 100% { + box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; + } +} +@-webkit-keyframes round { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes round { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } } \ No newline at end of file diff --git a/client/src/styles/waitforbump.css b/client/src/styles/waitforbump.css index 5f95cd6..3209ca6 100644 --- a/client/src/styles/waitforbump.css +++ b/client/src/styles/waitforbump.css @@ -3,15 +3,10 @@ .text-style{ font-family: 'Bangers', cursive; font-size: 36px; + margin-top: none !important; } .fists-bumping-container { - /* original css - position: absolute; - top: 47%; - left: 50%; - margin-top: -150px; - margin-left: -190px;*/ display: flex; flex-direction: column; align-items: center; @@ -19,13 +14,8 @@ } .fists-bumping-image-size{ - /* original css - margin-top: 30px; - max-width: 380px; - height: auto;*/ max-width: 380px; height: 200px; - margin-bottom: -90px; } .red-button-bottom{ @@ -33,25 +23,22 @@ font-size: 26px; position: relative; bottom: 5%; - /*left: 50%;*/ font-weight: 800; border-radius: 15px; max-width: 350px; min-width:350px; margin: 10px; - /*margin-left: -175px;*/ transition: transform 0.3s ease; background-color: rgba(255,180,176,255); } .test-button { - font-family: "Roboto", sans-serif; font-size: 23px; + border: none; font-weight: 800; - border-radius: 15px; - max-width: 175px; - min-width: 175px; - margin: 10px; + max-width: auto; + min-width: auto; + margin-top: 8%; transition: transform 0.3s ease; - background-color: rgba(153,153,255,255); + background-color: rgba(153,153,255,0); }