From 44d3450b1040b31442d1933e48b6fb663f6e0728 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 27 Oct 2019 18:06:47 -0400 Subject: [PATCH 1/9] Profile image upload frontend and backend finished --- functions/handlers/users.js | 53 +++++++- functions/index.js | 8 +- functions/package.json | 1 + functions/util/fbAuth.js | 12 +- twistter-frontend/src/pages/editProfile.js | 121 +++++++++++++++++- .../src/redux/actions/userActions.js | 23 +++- .../src/redux/reducers/userReducer.js | 8 +- 7 files changed, 210 insertions(+), 16 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 1af539d..5fc219c 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -53,6 +53,8 @@ exports.signup = (req, res) => { return res.status(400).json(errors); } + const noImg = 'no-img.png'; + let token, userId; db.doc(`/users/${newUser.handle}`) @@ -77,6 +79,7 @@ exports.signup = (req, res) => { email: newUser.email, handle: newUser.handle, createdAt: newUser.createdAt, + imageUrl: `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${noImg}?alt=media`, userId }; handle2Email.set(userCred.handle, userCred.email); @@ -207,7 +210,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 @@ -241,6 +244,7 @@ exports.getUserDetails = (req, res) => { }); }; +// Returns all data stored for a user exports.getAuthenticatedUser = (req, res) => { let credentials = {}; db.doc(`/users/${req.user.handle}`) @@ -258,4 +262,51 @@ exports.getAuthenticatedUser = (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 = {}; + + 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", () => { + admin.storage().bucket().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.status(201).json({ message: "Image uploaded successfully"}); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({ error: err.code}) + }) + }); + busboy.end(req.rawBody); +} diff --git a/functions/index.js b/functions/index.js index 737d44f..47dda28 100644 --- a/functions/index.js +++ b/functions/index.js @@ -16,7 +16,8 @@ const { login, signup, deleteUser, - updateProfileInfo + updateProfileInfo, + uploadProfileImage } = require("./handlers/users"); // Adds a user to the database and registers them in firebase with @@ -39,8 +40,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); + /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ diff --git a/functions/package.json b/functions/package.json index 2e308b3..492ff05 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/src/pages/editProfile.js b/twistter-frontend/src/pages/editProfile.js index ff6f3f7..39ad93f 100644 --- a/twistter-frontend/src/pages/editProfile.js +++ b/twistter-frontend/src/pages/editProfile.js @@ -4,13 +4,23 @@ import PropTypes from "prop-types"; // TODO: Add a read-only '@' in the left side of the handle input // TODO: Add a cancel button, that takes the user back to their profile page +import noImage from '../images/no-img.png'; + // Material-UI stuff import Button from "@material-ui/core/Button"; +import Box from "@material-ui/core/Box"; import CircularProgress from "@material-ui/core/CircularProgress"; import Grid from "@material-ui/core/Grid"; 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: { @@ -27,25 +37,52 @@ const styles = { positon: "relative", marginBottom: 30 }, + box: { + position: "relative" + }, progress: { position: "absolute" + }, + uploadProgress: { + position: "absolute", + marginLeft: -155, + marginTop: 95 } }; export class edit 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); 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 : "" }); }) .catch((err) => { @@ -63,6 +100,7 @@ export class edit extends Component { constructor() { super(); this.state = { + imageUrl: "", firstName: "", lastName: "", email: "", @@ -108,6 +146,7 @@ export class edit 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 @@ -128,10 +167,65 @@ export class edit extends Component { }); }; + handleImageChange = (event) => { + 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); + // } + render() { const { classes } = this.props; + const uploading = this.props.UI.loading; const { errors, loading } = this.state; + // let imageMarkup = this.state.imageUrl ? ( + // + // ) : (); + + let imageMarkup = this.props.user.credentials.imageUrl ? ( + + + {uploading && ( + + )} + + ) : ( + + + {uploading && ( + + )} + + ) + return ( @@ -140,6 +234,13 @@ export class edit extends Component { Edit Profile
+ {imageMarkup} + + + + + + Submit {loading && ( @@ -234,8 +335,18 @@ export class edit extends Component { } } +const mapStateToProps = (state) => ({ + user: state.user, + UI: state.UI, + // credentials: state.user.credentials +}); + +const mapActionsToProps = { uploadImage } + edit.propTypes = { + uploadImage: PropTypes.func.isRequired, classes: PropTypes.object.isRequired }; -export default withStyles(styles)(edit); +// export default withStyles(styles)(edit); +export default connect(mapStateToProps, mapActionsToProps)(withStyles(styles)(edit)); \ No newline at end of file diff --git a/twistter-frontend/src/redux/actions/userActions.js b/twistter-frontend/src/redux/actions/userActions.js index 5993804..41bd4bb 100644 --- a/twistter-frontend/src/redux/actions/userActions.js +++ b/twistter-frontend/src/redux/actions/userActions.js @@ -1,8 +1,9 @@ -import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED} from '../types'; +import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED, LOADING_USER} from '../types'; import axios from 'axios'; - +// Gets Database info for the logged in user and sets it in Redux export const getUserData = () => (dispatch) => { + dispatch({ type: LOADING_USER }); axios.get('/user') .then((res) => { dispatch({ @@ -13,6 +14,7 @@ export const getUserData = () => (dispatch) => { .catch((err) => console.error(err)); } +// Sends login data to firebase and sets the user data in Redux export const loginUser = (loginData, history) => (dispatch) => { dispatch({ type: LOADING_UI }); axios @@ -33,6 +35,7 @@ export const loginUser = (loginData, history) => (dispatch) => { }); }; +// Sends signup data to firebase and sets the user data in Redux export const signupUser = (newUserData, history) => (dispatch) => { dispatch({ type: LOADING_UI }); axios @@ -55,12 +58,14 @@ export const signupUser = (newUserData, history) => (dispatch) => { }); }; +// Deletes the Authorization header and clears all user data from Redux export const logoutUser = () => (dispatch) => { localStorage.removeItem('FBIdToken'); delete axios.defaults.headers.common['Authorization']; dispatch({ type: SET_UNAUTHENTICATED }); } + export const deleteUser = () => (dispatch) => { axios .delete("/delete") @@ -81,8 +86,22 @@ export const deleteUser = () => (dispatch) => { dispatch({ type: SET_UNAUTHENTICATED }); } +// Saves Authorization in browser local storage and adds it as a header to axios const setAuthorizationHeader = (token) => { const FBIdToken = `Bearer ${token}`; localStorage.setItem('FBIdToken', FBIdToken); axios.defaults.headers.common['Authorization'] = FBIdToken; +} + +// Sends an image data form to firebase to be uploaded to the user profile +export const uploadImage = (formData) => (dispatch) => { + dispatch({ type: LOADING_UI }); + axios.post('/user/image', formData) + .then(() => { + dispatch(getUserData()); + dispatch({ type: CLEAR_ERRORS }); + }) + .catch(err => { + console.log(err); + }) } \ No newline at end of file diff --git a/twistter-frontend/src/redux/reducers/userReducer.js b/twistter-frontend/src/redux/reducers/userReducer.js index 7a29e90..e5081f2 100644 --- a/twistter-frontend/src/redux/reducers/userReducer.js +++ b/twistter-frontend/src/redux/reducers/userReducer.js @@ -1,4 +1,4 @@ -import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED} from '../types'; +import {SET_USER, SET_ERRORS, CLEAR_ERRORS, LOADING_UI, SET_AUTHENTICATED, SET_UNAUTHENTICATED, LOADING_USER} from '../types'; const initialState = { authenticated: false, @@ -20,8 +20,14 @@ export default function(state = initialState, action) { case SET_USER: return { authenticated: true, + loading: false, ...action.payload, }; + case LOADING_USER: + return { + ...state, + loading: true + } default: return state; } From e8a403f575b009ef0794076e59abeeab2c074d36 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 27 Oct 2019 23:43:35 -0400 Subject: [PATCH 2/9] Delete old profile image --- functions/handlers/users.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 5fc219c..71c8783 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -273,6 +273,8 @@ exports.uploadProfileImage = (req, res) => { let imageFileName; let imageToBeUploaded = {}; + let oldImageFileName = req.userData.imageUrl.split("/o/")[1].split("?alt")[0]; + console.log(`old file: ${oldImageFileName}`); busboy.on("file", (fieldname, file, filename, encoding, mimetype) => { if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') { @@ -301,7 +303,19 @@ exports.uploadProfileImage = (req, res) => { return db.doc(`/users/${req.user.handle}`).update({ imageUrl }); }) .then(() => { - return res.status(201).json({ message: "Image uploaded successfully"}); + if (oldImageFileName !== "no-img.png") { + admin.storage().bucket().file(oldImageFileName).delete() + .then(() => { + return res.status(201).json({ message: "Image uploaded successfully"}); + }) + .catch((err) => { + console.log(err); + return res.status(201).json({ message: "Image uploaded successfully"}); + }) + } else { + return res.status(201).json({ message: "Image uploaded successfully"}); + } + }) .catch((err) => { console.error(err); From fb6c1e3d9864e28fa00fd5a91ca88bce57af34c4 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 29 Oct 2019 12:40:38 -0400 Subject: [PATCH 3/9] Image upload edit some return messages --- functions/handlers/users.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index d8429db..ec71b0f 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -1,4 +1,5 @@ /* eslint-disable promise/catch-or-return */ +/* eslint-disable promise/always-return */ const { admin, db } = require("../util/admin"); const config = require("../util/config"); @@ -307,14 +308,15 @@ exports.uploadProfileImage = (req, res) => { if (oldImageFileName !== "no-img.png") { admin.storage().bucket().file(oldImageFileName).delete() .then(() => { - return res.status(201).json({ message: "Image uploaded successfully"}); + return res.status(201).json({ message: "Image uploaded successfully1"}); }) .catch((err) => { console.log(err); - return res.status(201).json({ message: "Image uploaded successfully"}); + 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 successfully"}); + return res.status(201).json({ message: "Image uploaded successfully3"}); } }) From c96c77cd9f19932d580886019ed66f808bd0d4c2 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 29 Oct 2019 15:00:21 -0400 Subject: [PATCH 4/9] Debugging upload issues --- functions/handlers/users.js | 147 +++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 45 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index ec71b0f..4138134 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -266,64 +266,121 @@ exports.getAuthenticatedUser = (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 = 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.split("/o/")[1].split("?alt")[0]; + // 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", () => { + // admin.storage().bucket().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(() => { + // if (oldImageFileName !== "no-img.png") { + // admin.storage().bucket().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 imageFileName; let imageToBeUploaded = {}; - let oldImageFileName = req.userData.imageUrl.split("/o/")[1].split("?alt")[0]; - console.log(`old file: ${oldImageFileName}`); + let imageFileName; - busboy.on("file", (fieldname, file, filename, encoding, mimetype) => { + 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 filetype submitted" }); + return res.status(400).json({ error: 'Wrong file type 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 + // 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().upload(imageToBeUploaded.filepath, { - resumable: false, - metadata: { + busboy.on('finish', () => { + admin + .storage() + .bucket() + .upload(imageToBeUploaded.filepath, { + resumable: false, metadata: { - contentType: imageToBeUploaded.mimetype + 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(() => { - if (oldImageFileName !== "no-img.png") { - admin.storage().bucket().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}) - }) + }) + .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' }); + }); }); + try { busboy.end(req.rawBody); + } catch (err) { + return res.status(500).json({error: err}); + } } From ab07d45205b62c368e8809f26739e6b084933ba6 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Fri, 1 Nov 2019 10:12:54 -0400 Subject: [PATCH 5/9] Fix loading on image upload, so it waits for new image --- twistter-frontend/src/redux/actions/userActions.js | 5 +++-- twistter-frontend/src/redux/reducers/uiReducer.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/twistter-frontend/src/redux/actions/userActions.js b/twistter-frontend/src/redux/actions/userActions.js index 41bd4bb..5067258 100644 --- a/twistter-frontend/src/redux/actions/userActions.js +++ b/twistter-frontend/src/redux/actions/userActions.js @@ -9,7 +9,8 @@ export const getUserData = () => (dispatch) => { dispatch({ type: SET_USER, payload: res.data, - }) + }); + dispatch({type: CLEAR_ERRORS}); }) .catch((err) => console.error(err)); } @@ -99,7 +100,7 @@ export const uploadImage = (formData) => (dispatch) => { axios.post('/user/image', formData) .then(() => { dispatch(getUserData()); - dispatch({ type: CLEAR_ERRORS }); + // dispatch({ type: CLEAR_ERRORS }); }) .catch(err => { console.log(err); diff --git a/twistter-frontend/src/redux/reducers/uiReducer.js b/twistter-frontend/src/redux/reducers/uiReducer.js index 65e781d..491206e 100644 --- a/twistter-frontend/src/redux/reducers/uiReducer.js +++ b/twistter-frontend/src/redux/reducers/uiReducer.js @@ -23,7 +23,7 @@ export default function(state = initialState, action) { return { ...state, loading: true - } + }; default: return state; } From 1747a29c2e717d6b699d5f401dd4c4c459e891ac Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 3 Dec 2019 17:47:09 -0500 Subject: [PATCH 6/9] Fix not image not uploading when deployed --- functions/handlers/users.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 4138134..950fb0c 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -355,7 +355,7 @@ exports.uploadProfileImage = (req, res) => { busboy.on('finish', () => { admin .storage() - .bucket() + .bucket("twistter-e4649.appspot.com") .upload(imageToBeUploaded.filepath, { resumable: false, metadata: { @@ -378,9 +378,5 @@ exports.uploadProfileImage = (req, res) => { return res.status(500).json({ error: 'something went wrong' }); }); }); - try { - busboy.end(req.rawBody); - } catch (err) { - return res.status(500).json({error: err}); - } + busboy.end(req.rawBody); } From ebca5192e0c007ae3aa307e997af6de56c1231f2 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 3 Dec 2019 18:20:20 -0500 Subject: [PATCH 7/9] Change storageBucket to variable --- functions/handlers/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 950fb0c..71cb330 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -355,7 +355,7 @@ exports.uploadProfileImage = (req, res) => { busboy.on('finish', () => { admin .storage() - .bucket("twistter-e4649.appspot.com") + .bucket(config.storageBucket) .upload(imageToBeUploaded.filepath, { resumable: false, metadata: { From 79051f2243b0697a3b5226222a721983fb5be8a3 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Tue, 3 Dec 2019 23:46:04 -0500 Subject: [PATCH 8/9] Error checking and deleting old profile image --- functions/handlers/users.js | 201 +++++++++++---------- twistter-frontend/src/pages/editProfile.js | 10 +- 2 files changed, 108 insertions(+), 103 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 71cb330..73efa8a 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -266,117 +266,120 @@ exports.getAuthenticatedUser = (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.split("/o/")[1].split("?alt")[0]; - // 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", () => { - // admin.storage().bucket().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(() => { - // if (oldImageFileName !== "no-img.png") { - // admin.storage().bucket().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 = 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; + 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) => { - // console.log(fieldname, file, filename, encoding, mimetype); + busboy.on("file", (fieldname, file, filename, encoding, mimetype) => { if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') { - return res.status(400).json({ error: 'Wrong file type submitted' }); + return res.status(400).json({ error: "Wrong filetype 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}`; + // 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', () => { - admin - .storage() - .bucket(config.storageBucket) - .upload(imageToBeUploaded.filepath, { - resumable: false, + busboy.on("finish", () => { + // Save the file to the storage bucket + admin.storage().bucket(config.storageBucket).upload(imageToBeUploaded.filepath, { + resumable: false, + metadata: { metadata: { - metadata: { - contentType: imageToBeUploaded.mimetype - } + 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' }); - }); + } + }) + .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); + 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); } diff --git a/twistter-frontend/src/pages/editProfile.js b/twistter-frontend/src/pages/editProfile.js index 39ad93f..14b8632 100644 --- a/twistter-frontend/src/pages/editProfile.js +++ b/twistter-frontend/src/pages/editProfile.js @@ -168,10 +168,12 @@ export class edit extends Component { }; handleImageChange = (event) => { - const image = event.target.files[0]; - const formData = new FormData(); - formData.append('image', image, image.name); - this.props.uploadImage(formData); + 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 = () => { From 77a3ba9f69003cfb1a7a75c8ef3afc8be468fa2d Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Wed, 4 Dec 2019 00:30:23 -0500 Subject: [PATCH 9/9] Fix merge issues --- functions/handlers/post.js | 12 ++- functions/handlers/users.js | 3 - twistter-frontend/package.json | 2 +- twistter-frontend/src/pages/editProfile.js | 102 ++---------------- .../src/redux/actions/userActions.js | 8 +- 5 files changed, 19 insertions(+), 108 deletions(-) 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 a2de242..7d2b5c0 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -52,8 +52,6 @@ exports.signup = (req, res) => { return res.status(400).json(errors); } - const noImg = 'no-img.png'; - let token, userId; db.doc(`/users/${newUser.handle}`) @@ -79,7 +77,6 @@ exports.signup = (req, res) => { email: newUser.email, handle: newUser.handle, createdAt: newUser.createdAt, - imageUrl: `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${noImg}?alt=media`, userId, followedTopics: [], imageUrl: defaultImageUrl, 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 43b6e81..710636e 100644 --- a/twistter-frontend/src/pages/editProfile.js +++ b/twistter-frontend/src/pages/editProfile.js @@ -8,7 +8,6 @@ import noImage from '../images/no-img.png'; // Material-UI stuff import Box from "@material-ui/core/Box" import Button from "@material-ui/core/Button"; -import Box from "@material-ui/core/Box"; import CircularProgress from "@material-ui/core/CircularProgress"; import Grid from "@material-ui/core/Grid"; import Popover from "@material-ui/core/Popover"; @@ -253,96 +252,6 @@ export class editProfile extends Component { ) - return ( - - - - - Edit Profile - - - {imageMarkup} - - - - - - - - - - - - - - - - - -// ======= // Used for the delete button const open = Boolean(this.state.anchorEl); const id = open ? 'simple-popover' : undefined; @@ -353,7 +262,6 @@ export class editProfile extends Component { : -// >>>>>>> master