diff --git a/functions/handlers/post.js b/functions/handlers/post.js
index ae839c3..7cd966b 100644
--- a/functions/handlers/post.js
+++ b/functions/handlers/post.js
@@ -238,7 +238,8 @@ exports.quoteWithoutPost = (req, res) => {
}
})
.catch(err => {
- return res.status(500).json({ error: "Something is wrong" });
+ // return res.status(500).json({ error: "Something is wrong" });
+ return res.status(500).json({ error: err });
});
};
@@ -259,7 +260,11 @@ exports.checkforLikePost = (req, res) => {
result = true;
return res.status(200).json(result);
}
- });
+ })
+ .catch((err) => {
+ console.log(err);
+ return res.status(500).json({error: err});
+ })
};
exports.likePost = (req, res) => {
@@ -303,7 +308,8 @@ exports.likePost = (req, res) => {
}
})
.catch(err => {
- return res.status(500).json({ error: "Something is wrong" });
+ // return res.status(500).json({ error: "Something is wrong" });
+ return res.status(500).json({ error: err });
});
};
diff --git a/functions/handlers/users.js b/functions/handlers/users.js
index 7e63f32..7d2b5c0 100644
--- a/functions/handlers/users.js
+++ b/functions/handlers/users.js
@@ -1,3 +1,4 @@
+/* eslint-disable promise/catch-or-return */
/* eslint-disable promise/always-return */
const { admin, db } = require("../util/admin");
@@ -416,7 +417,7 @@ exports.updateProfileInfo = (req, res) => {
// Update the database entry for this user
db.collection("users")
.doc(req.user.handle)
- .set(profileData, { merge: true })
+ .set(profileData)
.then(() => {
console.log(`${req.user.handle}'s profile info has been updated.`);
return res.status(201).json({
@@ -467,6 +468,7 @@ exports.getAllHandles = (req, res) => {
});
};
+// Returns all data stored for a user
exports.getAuthenticatedUser = (req, res) => {
let credentials = {};
db.doc(`/users/${req.user.handle}`)
@@ -602,6 +604,126 @@ exports.getSubs = (req, res) => {
});
};
+// Uploads a profile image
+exports.uploadProfileImage = (req, res) => {
+ const BusBoy = require("busboy");
+ const path = require("path");
+ const os = require("os");
+ const fs = require("fs");
+
+ const busboy = new BusBoy({ headers: req.headers });
+
+ let imageFileName;
+ let imageToBeUploaded = {};
+ let oldImageFileName = req.userData.imageUrl ? req.userData.imageUrl.split("/o/")[1].split("?alt")[0] : null;
+ // console.log(`old file: ${oldImageFileName}`);
+
+ busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
+ if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
+ return res.status(400).json({ error: "Wrong filetype submitted" });
+ }
+ // console.log(fieldname);
+ // console.log(filename);
+ // console.log(mimetype);
+ const imageExtension = filename.split(".")[filename.split(".").length - 1]; // Get the image file extension
+ imageFileName = `${Math.round(Math.random() * 100000000000)}.${imageExtension}`; // Get a random filename
+ const filepath = path.join(os.tmpdir(), imageFileName);
+ imageToBeUploaded = { filepath, mimetype };
+ file.pipe(fs.createWriteStream(filepath));
+ });
+ busboy.on("finish", () => {
+ // Save the file to the storage bucket
+ admin.storage().bucket(config.storageBucket).upload(imageToBeUploaded.filepath, {
+ resumable: false,
+ metadata: {
+ metadata: {
+ contentType: imageToBeUploaded.mimetype
+ }
+ }
+ })
+ .then(() => {
+ // Add the new URL to the user's profile
+ const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
+ return db.doc(`/users/${req.user.handle}`).update({ imageUrl });
+ })
+ .then(() => {
+ // Delete their old image if they have one
+ if (oldImageFileName !== null && oldImageFileName !== "no-img.png") {
+ admin.storage().bucket(config.storageBucket).file(oldImageFileName).delete()
+ .then(() => {
+ return res.status(201).json({ message: "Image uploaded successfully1"});
+ })
+ .catch((err) => {
+ console.log(err);
+ return res.status(201).json({ message: "Image uploaded successfully2"});
+ })
+ // return res.status(201).json({ message: "Image uploaded successfully"});
+ } else {
+ return res.status(201).json({ message: "Image uploaded successfully3"});
+ }
+
+ })
+ .catch((err) => {
+ console.error(err);
+ return res.status(500).json({ error: err.code})
+ })
+ });
+ busboy.end(req.rawBody);
+
+ // const BusBoy = require('busboy');
+ // const path = require('path');
+ // const os = require('os');
+ // const fs = require('fs');
+
+ // const busboy = new BusBoy({ headers: req.headers });
+
+ // let imageToBeUploaded = {};
+ // let imageFileName;
+
+ // busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
+ // // console.log(fieldname, file, filename, encoding, mimetype);
+ // if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
+ // return res.status(400).json({ error: 'Wrong file type submitted' });
+ // }
+ // // my.image.png => ['my', 'image', 'png']
+ // const imageExtension = filename.split('.')[filename.split('.').length - 1];
+ // // 32756238461724837.png
+ // imageFileName = `${Math.round(
+ // Math.random() * 1000000000000
+ // ).toString()}.${imageExtension}`;
+ // const filepath = path.join(os.tmpdir(), imageFileName);
+ // imageToBeUploaded = { filepath, mimetype };
+ // file.pipe(fs.createWriteStream(filepath));
+ // });
+ // busboy.on('finish', () => {
+ // admin
+ // .storage()
+ // .bucket(config.storageBucket)
+ // .upload(imageToBeUploaded.filepath, {
+ // resumable: false,
+ // metadata: {
+ // metadata: {
+ // contentType: imageToBeUploaded.mimetype
+ // }
+ // }
+ // })
+ // .then(() => {
+ // const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${
+ // config.storageBucket
+ // }/o/${imageFileName}?alt=media`;
+ // return db.doc(`/users/${req.user.handle}`).update({ imageUrl });
+ // })
+ // .then(() => {
+ // return res.json({ message: 'image uploaded successfully' });
+ // })
+ // .catch((err) => {
+ // console.error(err);
+ // return res.status(500).json({ error: 'something went wrong' });
+ // });
+ // });
+ // busboy.end(req.rawBody);
+}
+
exports.removeSub = (req, res) => {
let new_following = [];
let userRef = db.doc(`/users/${req.userData.handle}`);
diff --git a/functions/index.js b/functions/index.js
index 41cc61a..4f7c3fb 100644
--- a/functions/index.js
+++ b/functions/index.js
@@ -18,6 +18,7 @@ const {
signup,
deleteUser,
updateProfileInfo,
+ uploadProfileImage,
verifyUser,
unverifyUser,
getUserHandles,
@@ -50,8 +51,13 @@ app.get("/getProfileInfo", fbAuth, getProfileInfo);
// Updates the currently logged in user's profile information
app.post("/updateProfileInfo", fbAuth, updateProfileInfo);
+// Returns all user data for the logged in user.
+// Used when setting the state in Redux.
app.get("/user", fbAuth, getAuthenticatedUser);
+// Uploads a profile image
+app.post("/user/image", fbAuth, uploadProfileImage);
+
// Verifies the user sent to the request
// Must be run by the Admin user
app.post("/verifyUser", fbAuth, verifyUser);
diff --git a/functions/package.json b/functions/package.json
index 29b3e89..04a805c 100644
--- a/functions/package.json
+++ b/functions/package.json
@@ -14,6 +14,7 @@
},
"dependencies": {
"axios": "^0.19.0",
+ "busboy": "^0.3.1",
"firebase": "^6.6.2",
"firebase-admin": "^8.6.0",
"firebase-functions": "^3.1.0",
diff --git a/functions/util/fbAuth.js b/functions/util/fbAuth.js
index 35253e7..d440bb9 100644
--- a/functions/util/fbAuth.js
+++ b/functions/util/fbAuth.js
@@ -4,12 +4,12 @@ const { admin, db } = require('./admin');
// The function will only execute if the user is logged in, or rather, they have
// a valid token
module.exports = (req, res, next) => {
- console.log(req);
- console.log(req.body);
- console.log(req.headers);
- console.log(req.headers.authorization);
- console.log(JSON.stringify(req.body));
- console.log(JSON.stringify(req.header));
+ // console.log(req);
+ // console.log(req.body);
+ // console.log(req.headers);
+ // console.log(req.headers.authorization);
+ // console.log(JSON.stringify(req.body));
+ // console.log(JSON.stringify(req.header));
let idToken;
diff --git a/twistter-frontend/package.json b/twistter-frontend/package.json
index 52e50f3..3ac341d 100644
--- a/twistter-frontend/package.json
+++ b/twistter-frontend/package.json
@@ -43,5 +43,5 @@
"last 1 safari version"
]
},
- "proxy": "http://localhost:5001/twistter-e4649/us-central1/api"
+ "proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api"
}
diff --git a/twistter-frontend/src/pages/editProfile.js b/twistter-frontend/src/pages/editProfile.js
index b4f7c2e..710636e 100644
--- a/twistter-frontend/src/pages/editProfile.js
+++ b/twistter-frontend/src/pages/editProfile.js
@@ -3,6 +3,8 @@ import { Link } from 'react-router-dom';
import axios from "axios";
import PropTypes from "prop-types";
+import noImage from '../images/no-img.png';
+
// Material-UI stuff
import Box from "@material-ui/core/Box"
import Button from "@material-ui/core/Button";
@@ -12,6 +14,13 @@ import Popover from "@material-ui/core/Popover";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import withStyles from "@material-ui/core/styles/withStyles";
+import IconButton from "@material-ui/core/IconButton";
+import EditIcon from "@material-ui/icons/Edit";
+import Tooltip from "@material-ui/core/Tooltip";
+
+// Redux stuff
+import { connect } from "react-redux";
+import { uploadImage } from "../redux/actions/userActions";
const styles = {
form: {
@@ -28,6 +37,9 @@ const styles = {
positon: "relative",
marginBottom: 30
},
+ box: {
+ position: "relative"
+ },
back: {
float: "left",
marginLeft: 15
@@ -39,6 +51,11 @@ const styles = {
progress: {
position: "absolute"
},
+ uploadProgress: {
+ position: "absolute",
+ marginLeft: -155,
+ marginTop: 95
+ },
popoverBackground: {
marginTop: "-100px",
width: "calc(100vw)",
@@ -50,20 +67,38 @@ const styles = {
};
export class editProfile extends Component {
+ // mapReduxToState = (credentials) => {
+ // this.setState({
+ // imageUrl: credentials.imageUrl ? credentials.imageUrl : noImage,
+ // firstName: credentials.firstName ? credentials.firstName : '',
+ // lastName: credentials.lastName ? credentials.lastName : '',
+ // email: credentials.email ? credentials.email : 'error, email doesn\'t exist',
+ // handle: credentials.handle ? credentials.handle : 'error, handle doesn\'t exist',
+ // bio: credentials.bio ? credentials.bio : ''
+ // });
+ // };
+
// Runs as soon as the page loads.
// Sets the default values of all the textboxes to the data
// that is stored in the database for the user.
componentDidMount() {
+ // const { credentials } = this.props;
+ // console.log(this.props.user);
+ // this.mapReduxToState(credentials);
this.setState({pageLoading: true})
+
axios
.get("/getProfileInfo")
.then((res) => {
+ // Need to have the ternary if statements, because react throws an error if
+ // any of the res.data keys are undefined
this.setState({
- firstName: res.data.firstName,
- lastName: res.data.lastName,
+ imageUrl: res.data.imageUrl,
+ firstName: res.data.firstName ? res.data.firstName : "",
+ lastName: res.data.lastName ? res.data.lastName : "",
email: res.data.email,
handle: res.data.handle,
- bio: res.data.bio,
+ bio: res.data.bio ? res.data.bio : "",
pageLoading: false
});
})
@@ -80,6 +115,7 @@ export class editProfile extends Component {
constructor() {
super();
this.state = {
+ imageUrl: "",
firstName: "",
lastName: "",
email: "",
@@ -126,6 +162,7 @@ export class editProfile extends Component {
})
.catch((err) => {
console.log(err);
+ // TODO: Should redirect to login page if they get a 403
this.setState({
errors: err.response.data,
loading: false
@@ -146,6 +183,26 @@ export class editProfile extends Component {
});
};
+ handleImageChange = (event) => {
+ if (event.target.files[0]) {
+ const image = event.target.files[0];
+ const formData = new FormData();
+ formData.append('image', image, image.name);
+ this.props.uploadImage(formData);
+ }
+ }
+
+ handleEditPicture = () => {
+ const fileInput = document.getElementById('imageUpload');
+ fileInput.click();
+ }
+
+ // logging = () => {
+ // console.log(this.state);
+ // console.log(this.props);
+ // this.mapReduxToState(this.props.credentials);
+ // }
+
handleOpenConfirmDelete = (event) => {
this.setState({
// anchorEl: event.currentTarget
@@ -162,8 +219,39 @@ export class editProfile extends Component {
render() {
const { classes } = this.props;
+ const uploading = this.props.UI.loading;
const { errors, loading } = this.state;
+// <<<<<<< edit-profile-image-upload
+
+ let imageMarkup = this.props.user.credentials.imageUrl ? (
+
+
+ {uploading && (
+
+ )}
+
+ ) : (
+
+
+ {uploading && (
+
+ )}
+
+ )
+
// Used for the delete button
const open = Boolean(this.state.anchorEl);
const id = open ? 'simple-popover' : undefined;
@@ -177,6 +265,8 @@ export class editProfile extends Component {