Compare commits

...

90 Commits

Author SHA1 Message Date
Danny Voltz
14e09d88ea Fixed merge issue 2019-10-03 09:41:47 -05:00
asankaran35
c14263988e Merge pull request #25 from ClaytonWWilson/fix
fixed login and register. should be working on frontend now
2019-10-03 09:31:19 -04:00
Aditya Sankaran
64657fa3af fixed login and register. working on frontend now 2019-10-03 09:29:22 -04:00
Leon Liang
d072a5ff95 Merge pull request #24 from ClaytonWWilson/profile_display
Profile display
2019-10-02 23:30:05 -04:00
Leon Liang
9c0dac507f Light blue color scheme 2019-10-02 23:29:05 -04:00
Leon Liang
5168de65c1 Merge branch 'master' into profile_display 2019-10-02 23:24:21 -04:00
Leon Liang
2536547c63 Theming 2019-10-02 23:21:30 -04:00
Leon Liang
42a07ffa5e resolved merge conflict in app.css 2019-10-02 23:10:12 -04:00
Leon Liang
7558602a90 Added post card 2019-10-02 23:08:34 -04:00
Aditya Sankaran
3b682bae18 Revert "Can successfully register and login in UI"
This reverts commit 893cad7e7e.

reverting commit
2019-10-02 21:38:23 -04:00
Aditya Sankaran
5acc7a64e0 Revert "Modified code for posts in homes"
This reverts commit 71b6965830.

   reversing commit
2019-10-02 21:35:23 -04:00
Aaron Sun
71b6965830 Modified code for posts in homes 2019-10-02 20:45:45 -04:00
Aaron Sun
893cad7e7e Can successfully register and login in UI 2019-10-02 20:40:46 -04:00
8f949f14be Fix caseSensitive error and moment import 2019-10-02 18:00:29 -04:00
Danny Voltz
39098e917f pull 2019-10-02 16:37:10 -05:00
Danny Voltz
29c07a9b14 commit 2019-10-02 16:24:01 -05:00
Leon Liang
15f106ed1a added theme.js 2019-10-02 15:10:02 -04:00
asankaran35
e110bdd47a Merge pull request #21 from ClaytonWWilson/user-line-posts
finished rendering userline for user and changed character limit for …
2019-10-02 15:01:00 -04:00
Aditya Sankaran
b7f0eba7a9 finished rendering userline for user and changed character limit for writing microblogs 2019-10-02 14:59:18 -04:00
878bb28a71 Merge pull request #20 from ClaytonWWilson/edit-profile-frontend
Edit profile frontend
2019-10-01 21:01:29 -04:00
88310714b2 Merge branch 'master' into edit-profile-frontend 2019-10-01 21:01:02 -04:00
c218081759 Making everything look pretty 2019-10-01 20:39:21 -04:00
asankaran35
16973fb340 Merge pull request #19 from ClaytonWWilson/user-line
added entering topics field and database call for adding topics when …
2019-10-01 19:16:02 -04:00
Aditya Sankaran
9d4f175a49 added entering topics field and database call for adding topics when writing microblog 2019-10-01 19:07:57 -04:00
Leon Liang
f627b91543 change case sensitive file name 2019-10-01 19:04:03 -04:00
Leon Liang
ec6e41d16f Merge pull request #18 from ClaytonWWilson/file_org
File org for twistter-frontend js files
2019-10-01 19:00:40 -04:00
Leon Liang
2e157b9088 Merge branch 'master' into file_org 2019-10-01 19:00:19 -04:00
Leon Liang
89e8942624 file organization for twistter frontend 2019-10-01 18:54:26 -04:00
Leon Liang
b9909c1616 move home.js and so on into according folders 2019-10-01 18:44:49 -04:00
Danny Voltz
fadb0f0399 added logout support 2019-10-01 17:34:19 -05:00
Danny Voltz
931929061f Merge branch 'master' of https://github.com/ClaytonWWilson/CS307-Team24 2019-10-01 17:30:39 -05:00
Danny Voltz
8eb0872d16 logout ui 2019-10-01 17:27:52 -05:00
07e9271132 Removing hard-coded user info. 2019-10-01 17:30:18 -04:00
724adc9b0b Merge branch 'master' of https://github.com/ClaytonWWilson/CS307-Team24 2019-10-01 17:02:05 -04:00
b711521b4b Fix unnecessary escape deploy error. 2019-10-01 17:01:59 -04:00
967e66313b Merge pull request #17 from ClaytonWWilson/edit-profile-frontend
Edit profile frontend
2019-10-01 16:56:58 -04:00
76caf4de12 Merge branch 'master' into edit-profile-frontend 2019-10-01 16:54:13 -04:00
eb945fa0cf Finished fixing login and signup backend. 2019-10-01 16:31:37 -04:00
c5bec46b52 Fix fbAuth function not extracting 'Bearer ' 2019-10-01 16:08:53 -04:00
cc98fa7884 Fixed index.js and refactored it. 2019-10-01 15:27:24 -04:00
Leon Liang
101d9918e5 Changes to navigation bar 2019-10-01 14:43:41 -04:00
5116a705bc Save before trying to fix master. 2019-10-01 14:32:27 -04:00
Aditya Sankaran
311e8b3716 Merge branch 'master' of https://github.com/ClaytonWWilson/CS307-Team24 2019-10-01 14:03:37 -04:00
Aditya Sankaran
754c2f2d75 added get request userline function 2019-10-01 14:03:15 -04:00
Aaron Sun
13b57eedc6 Merge branch 'master' of https://github.com/ClaytonWWilson/CS307-Team24 2019-10-01 13:57:44 -04:00
Leon Liang
60d1d76839 Added nav bar 2019-10-01 12:56:34 -04:00
Leon Liang
e273b1474b Merge branch 'master' of github.com:ClaytonWWilson/CS307-Team24
added Navigation bar
2019-10-01 12:45:10 -04:00
Leon Liang
022f440cb9 added Navigation bar to home page 2019-10-01 12:44:47 -04:00
721aeeb350 Edit Profile front-end finished and connected to back-end 2019-09-30 23:39:35 -04:00
Aaron Sun
4517bc6756 Reconciled with new pull from master 2019-09-30 23:22:09 -04:00
Aaron Sun
201f5d8949 Reconciled my code with master 2019-09-30 23:04:37 -04:00
696b00e304 Edit profile front-end almost finished. 2019-09-30 22:30:29 -04:00
Aaron Sun
b1cfcda948 Clean baseline 2019-09-30 22:26:28 -04:00
asankaran35
7969d3b10b Merge pull request #16 from ClaytonWWilson/userline
Userline
2019-09-30 18:23:36 -04:00
asankaran35
d298843268 Merge branch 'master' into userline 2019-09-30 18:23:21 -04:00
Aditya Sankaran
55dc62e57e connected backend to frontend for writing microblogs 2019-09-30 18:06:23 -04:00
d255a301a3 Merge branch 'edit-profile-frontend' of https://github.com/ClaytonWWilson/CS307-Team24 into edit-profile-frontend 2019-09-30 00:04:23 -04:00
0f127521ca Added edit page. 2019-09-30 00:04:06 -04:00
90442fe3cd Fixing 'firebase deploy' errors and warnings 2019-09-29 23:59:36 -04:00
2dc89b99b6 Added edit page. 2019-09-29 23:31:33 -04:00
5176f0250c Merge pull request #15 from ClaytonWWilson/edit-profile-frontend
Changed naming from FBAuth to fbAuth
2019-09-29 22:07:25 -04:00
929216c05e Changed naming from FBAuth to fbAuth 2019-09-29 22:06:51 -04:00
dc498a3a1d Add comments to fbAuth.js 2019-09-29 21:56:59 -04:00
f4ef1ee8e1 Wrong filename 2019-09-29 21:52:25 -04:00
ce8ee36354 Fixed missing bracket 2019-09-29 21:46:18 -04:00
13fcd9b3ab Merge pull request #14 from ClaytonWWilson/edit-profile-info
Edit profile info
2019-09-29 21:35:54 -04:00
Clayton Wilson
7f2d6a3a1e Merge branch 'master' into edit-profile-info 2019-09-29 21:33:41 -04:00
d05f87c7a7 Made edit profile data validation more robust. 2019-09-29 21:01:06 -04:00
Aditya Sankaran
b1faacd312 backend and frontend connect in progress 2019-09-29 19:07:35 -04:00
15f976398a Code refactoring 2019-09-29 01:35:06 -04:00
262e59df65 Disabled the feed route rewrite and small fixes. 2019-09-28 22:42:26 -04:00
Aditya Sankaran
cbf9b94248 added home router page 2019-09-28 14:07:52 -04:00
Leon Liang
6cb3441ad7 Merge pull request #13 from ClaytonWWilson/profile_display
change the location of logo.png so that it displays correctly
2019-09-27 22:04:16 -04:00
Leon Liang
015f1c1e9f change the location of logo.png so that it displays correctly 2019-09-27 21:59:57 -04:00
Leon Liang
2d00090988 Merge pull request #12 from ClaytonWWilson/user_subscription
Re-organized file structures
2019-09-27 21:42:22 -04:00
Leon Liang
cc1ce1bfc9 Merge branch 'master' into user_subscription 2019-09-27 21:40:39 -04:00
Leon Liang
1306c1654a restructure files 2019-09-27 19:35:33 -04:00
Leon Liang
5caea0871e Modularize code and added NavBar component 2019-09-27 19:34:42 -04:00
5642b685bc Finished functionality of backend edit profile functions 2019-09-27 16:28:00 -04:00
asankaran35
8a5b115666 Merge pull request #11 from ClaytonWWilson/write_microblogs
writing and posting microblog
2019-09-27 12:52:05 -04:00
Leon Liang
e961379e3f added new files for structural purpose 2019-09-26 13:59:39 -04:00
Leon Liang
d703843277 added Navbar, profile and post skeleton 2019-09-26 13:58:26 -04:00
Leon Liang
70037a5342 added folders to organize files 2019-09-26 01:54:51 -04:00
Leon Liang
8fa03700b1 Refactored and organized files based on functionality 2019-09-26 01:49:19 -04:00
Aaron Sun
a930353d79 Merge branch 'master' of https://github.com/ClaytonWWilson/CS307-Team24 2019-09-24 14:52:25 -04:00
Aaron Sun
e2883a04c5 In the process of linking backend and UI 2019-09-24 14:38:13 -04:00
Aaron Sun
6f94623d0c Modified the code structure for the auth UI 2019-09-24 08:38:02 -04:00
Clayton Wilson
bcdd452aa8 Start of the edit profile functions. 2019-09-23 22:58:06 -04:00
Clayton Wilson
4fbacaf822 Merge pull request #10 from ClaytonWWilson/edit-profile-info
Added the FBAuth middleman function for authentication.
2019-09-23 21:20:05 -04:00
Clayton Wilson
709af8007d Added the FBAuth middleman function for authentication. 2019-09-23 21:18:33 -04:00
45 changed files with 5769 additions and 2098 deletions

View File

@@ -5,4 +5,4 @@
"Twistter1": "twistter-6a42d", "Twistter1": "twistter-6a42d",
"SuperTiger69": "twistter-e4649" "SuperTiger69": "twistter-e4649"
} }
} }

3
.gitignore vendored
View File

@@ -63,3 +63,6 @@ node_modules/
# dotenv environment variables file # dotenv environment variables file
.env .env
# The keyfile for google services authentication
twistter-e4649-firebase-adminsdk-pgjve-1e57494429.json

View File

@@ -1,29 +1,30 @@
{ {
"database": { "database": {
"rules": "database.rules.json" "rules": "database.rules.json"
}, },
"firestore": { "firestore": {
"rules": "firestore.rules", "rules": "firestore.rules",
"indexes": "firestore.indexes.json" "indexes": "firestore.indexes.json"
}, },
"functions": { "functions": {
"predeploy": [ "predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint" "npm --prefix \"$RESOURCE_DIR\" run lint"
] ]
}, },
"hosting": { "hosting": {
"public": "public", "public": "public",
"ignore": [ "ignore": [
"firebase.json", "firebase.json",
"**/.*", "**/.*",
"**/node_modules/**" "**/node_modules/**"
], ],
"rewrites": [{ "rewrites": [{
"source": "/feed", "source": "/feed",
"destination": "/feed.html" "destination": "/feed.html"
}] }]
}, },
"storage": { "storage": {
"rules": "storage.rules" "rules": "storage.rules"
}
} }
}

View File

@@ -0,0 +1,43 @@
/* eslint-disable promise/always-return */
const admin = require('firebase-admin');
exports.putPost = (req, res) => {
const newPost = {
body: req.body.body,
userHandle: req.body.userHandle,
userImage: req.body.userImage,
microBlogTitle: req.body.microBlogTitle,
createdAt: new Date().toISOString(),
likeCount: 0,
commentCount: 0,
microBlogTopics: req.body.microBlogTopics
};
admin.firestore().collection('posts').add(newPost)
.then((doc) => {
const resPost = newPost;
resPost.postId = doc.id;
return res.status(200).json(resPost);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: 'something is wrong'});
});
};
exports.getallPostsforUser = (req, res) => {
admin.firestore().collection('posts').where('userHandle', '==', 'new user' ).get()
.then((data) => {
let posts = [];
data.forEach(function(doc) {
posts.push(doc.data());
});
return res.status(200).json(posts);
})
.catch((err) => {
console.error(err);
return res.status(500).json({error: 'Failed to fetch all posts written by specific user.'})
})
}

216
functions/handlers/users.js Normal file
View File

@@ -0,0 +1,216 @@
/* eslint-disable promise/catch-or-return */
const { admin, db } = require("../util/admin");
const config = require("../util/config");
const { validateUpdateProfileInfo } = require("../util/validator");
const firebase = require("firebase");
firebase.initializeApp(config);
exports.signup = (req, res) => {
const newUser = {
email: req.body.email,
handle: req.body.handle,
password: req.body.password,
confirmPassword: req.body.confirmPassword,
createdAt: new Date().toISOString()
};
let errors = {};
const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// Email check
if (newUser.email.trim() === "") {
errors.email = "Email must not be blank.";
} else if (!newUser.email.match(emailRegEx)) {
errors.email = "Email is invalid.";
}
// handle check
if (newUser.handle.trim() === "") {
errors.handle = "Username must not be blank.";
} else if (newUser.handle.length < 4 || newUser.handle.length > 30) {
errors.handle = "Username must be between 4-30 characters long.";
}
// Password check
if (newUser.password.trim() === "") {
errors.password = "Password must not be blank.";
} else if (newUser.password.length < 8 || newUser.password.length > 20) {
errors.password = "Password must be between 8-20 characters long.";
}
// Confirm password check
if (newUser.confirmPassword !== newUser.password) {
errors.confirmPassword = "Passwords must match.";
}
// Overall check
if (Object.keys(errors).length > 0) {
return res.status(400).json(errors);
}
let idToken, userId;
db.doc(`/users/${newUser.handle}`)
.get()
.then((doc) => {
if (doc.exists) {
return res
.status(400)
.json({ handle: "This username is already taken." });
}
return firebase
.auth()
.createUserWithEmailAndPassword(newUser.email, newUser.password);
})
.then((data) => {
userId = data.user.uid;
return data.user.getIdToken();
})
.then((token) => {
idToken = token;
const userCred = {
email: req.body.email,
handle: newUser.handle,
createdAt: newUser.createdAt,
userId
};
return db.doc(`/users/${newUser.handle}`).set(userCred);
})
.then(() => {
return res.status(201).json({ idToken });
})
.catch((err) => {
console.error(err);
if (err.code === "auth/email-already-in-use") {
return res.status(500).json({ email: "This email is already taken." });
}
return res.status(500).json({ error: err.code });
});
};
exports.login = (req, res) => {
const user = {
email: req.body.email,
password: req.body.password
};
// Auth validation
let errors = {};
// Email check
if (user.email.trim() === "") {
errors.email = "Email must not be blank.";
}
// Password check
if (user.password.trim() === "") {
errors.password = "Password must not be blank.";
}
// Checking if any errors have been raised
if (Object.keys(errors).length > 0) {
return res.status(400).json(errors);
}
firebase
.auth()
.signInWithEmailAndPassword(user.email, user.password)
.then((data) => {
return data.user.getIdToken();
})
.then((token) => {
return res.json({ token });
})
.catch((err) => {
console.error(err);
if (err.code === "auth/wrong-password") {
return res
.status(403)
.json({ general: "Invalid credentials. Please try again." });
}
return res.status(500).json({ error: err.code });
});
};
// Returns all data in the database for the user who is currently signed in
exports.getProfileInfo = (req, res) => {
db.collection("users")
.doc(req.user.handle)
.get()
.then((data) => {
return res.status(200).json(data.data());
})
.catch((err) => {
console.error(err);
return res.status(500).json(err);
});
};
// Updates the data in the database of the user who is currently logged in
exports.updateProfileInfo = (req, res) => {
// TODO: Add functionality for adding/updating profile images
// Data validation
const { valid, errors, profileData } = validateUpdateProfileInfo(req.body);
if (!valid) return res.status(400).json(errors);
// Update the database entry for this user
db.collection("users")
.doc(req.user.handle)
.set(profileData, { merge: true })
.then(() => {
console.log(`${req.user.handle}'s profile info has been updated.`);
return res
.status(201)
.json({
general: `${req.user.handle}'s profile info has been updated.`
});
})
.catch((err) => {
console.error(err);
return res.status(500).json({
error: "Error updating profile data"
});
});
};
exports.getUserDetails = (req, res) => {
let userData = {};
db.doc(`/users/${req.params.handle}`)
.get()
.then((doc) => {
if (doc.exists) {
userData.user = doc.data();
return db
.collection("post")
.where("userHandle", "==", req.params.handle)
.orderBy("createdAt", "desc")
.get();
} else {
return res.status(404).json({
error: "User not found"
});
}
})
.then((data) => {
userData.posts = [];
data.forEach((doc) => {
userData.posts.push({
body: doc.data().body,
createAt: doc.data().createAt,
userHandle: doc.data().userHandle,
userImage: doc.data().userImage,
likeCount: doc.data().likeCount,
commentCount: doc.data().commentCount,
postId: doc.id
});
});
return res.json(userData);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};

View File

@@ -1,43 +1,47 @@
/* eslint-disable promise/always-return */ /* eslint-disable promise/always-return */
const functions = require('firebase-functions'); const app = require("express")();
const admin = require('firebase-admin'); const cors = require("cors");
const app = require('express')(); const { db } = require("./util/admin");
admin.initializeApp(); const fbAuth = require("./util/fbAuth");
const functions = require("firebase-functions");
app.use(cors());
const firebaseConfig = { /*------------------------------------------------------------------*
apiKey: "AIzaSyCvsWetg4qFdsPGfJ3LCw_QaaYzoan7Q34", * handlers/users.js *
authDomain: "twistter-e4649.firebaseapp.com", *------------------------------------------------------------------*/
databaseURL: "https://twistter-e4649.firebaseio.com", const {
projectId: "twistter-e4649", getUserDetails,
storageBucket: "twistter-e4649.appspot.com", getProfileInfo,
messagingSenderId: "20131817365", login,
appId: "1:20131817365:web:633c95fb08b16d4526b89c" signup,
}; updateProfileInfo
const firebase = require('firebase'); } = require("./handlers/users");
firebase.initializeApp(firebaseConfig);
app.get('/getUsers', (req, res) => { // Adds a user to the database and registers them in firebase with
admin.firestore().collection('users').get().then(data => { // an email and password pair
let users = []; // Returns a token for the new user
data.forEach(doc => { app.post("/signup", signup);
users.push(doc.data());
}); return res.json(users);
}).catch((err) => console.error(err));
});
app.post('/postUser', (req, res) => { // Returns a token for the user that matches the provided username
const newUser = { // and password
body: req.body.body app.post("/login", login);
};
admin.firestore().collection('users').add(newUser).then((doc) => { app.get("/getUser/:handle", getUserDetails);
res.json({
message: 'Successfully added!' // Returns all profile data of the currently logged in user
}); app.get("/getProfileInfo", fbAuth, getProfileInfo);
}).catch((err) => {
res.status(500).json({ // Updates the currently logged in user's profile information
error: "Error in posting user!" app.post("/updateProfileInfo", fbAuth, updateProfileInfo);
});
console.error(err); /*------------------------------------------------------------------*
}); * handlers/post.js *
}); *------------------------------------------------------------------*/
exports.api = functions.https.onRequest(app); const { getallPostsforUser, putPost } = require("./handlers/post");
app.get("/getallPostsforUser", getallPostsforUser);
// Adds one post to the database
app.post("/putPost", fbAuth, putPost);
exports.api = functions.https.onRequest(app);

View File

@@ -24,16 +24,31 @@
} }
}, },
"@firebase/app": { "@firebase/app": {
"version": "0.4.16", "version": "0.4.17",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.4.16.tgz", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.4.17.tgz",
"integrity": "sha512-4aa6ixQlV6xQxj4HbwFKrfYZnnKk8AtB/vEEuIaBCGQYBvV287OVNCozXd4CC4Q4I4Vtkzrc+kggahYFl8nDWQ==", "integrity": "sha512-YkCe10/KHnfJ5Lx79SCQ4ZJRlpnwe8Yns6Ntf7kltXq1hCQCUrKEU3zaOTPY90SBx36hYm47IaqkKwT/kBOK3A==",
"requires": { "requires": {
"@firebase/app-types": "0.4.3", "@firebase/app-types": "0.4.3",
"@firebase/logger": "0.1.24", "@firebase/logger": "0.1.25",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"dom-storage": "2.1.0", "dom-storage": "2.1.0",
"tslib": "1.10.0", "tslib": "1.10.0",
"xmlhttprequest": "1.8.0" "xmlhttprequest": "1.8.0"
},
"dependencies": {
"@firebase/logger": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz",
"integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w=="
},
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/app-types": { "@firebase/app-types": {
@@ -55,15 +70,36 @@
"integrity": "sha512-foQHhvyB0RR+mb/+wmHXd/VOU+D8fruFEW1k79Q9wzyTPpovMBa1Mcns5fwEWBhUfi8bmoEtaGB8RSAHnTFzTg==" "integrity": "sha512-foQHhvyB0RR+mb/+wmHXd/VOU+D8fruFEW1k79Q9wzyTPpovMBa1Mcns5fwEWBhUfi8bmoEtaGB8RSAHnTFzTg=="
}, },
"@firebase/database": { "@firebase/database": {
"version": "0.5.2", <<<<<<< HEAD
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.2.tgz", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.3.tgz",
"integrity": "sha512-LnXKRE1AmjlS+iRF7j8vx+Ni8x85CmLP5u5Pw5rDKhKLn2eTR1tJKD937mUeeGEtDHwR1rrrkLYOqRR2cSG3hQ==", "integrity": "sha512-LnXKRE1AmjlS+iRF7j8vx+Ni8x85CmLP5u5Pw5rDKhKLn2eTR1tJKD937mUeeGEtDHwR1rrrkLYOqRR2cSG3hQ==",
=======
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.4.tgz",
"integrity": "sha512-Hz1Bi3fzIcNNocE4EhvvwoEQGurG2BGssWD3/6a2bzty+K1e57SLea2Ied8QYNBUU1zt/4McHfa3Y71EQIyn/w==",
>>>>>>> 7969d3b10bc35a9078834c5ee2ba8c8fd60d338f
"requires": { "requires": {
"@firebase/database-types": "0.4.3", "@firebase/database-types": "0.4.3",
"@firebase/logger": "0.1.24", "@firebase/logger": "0.1.25",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"faye-websocket": "0.11.3", "faye-websocket": "0.11.3",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/logger": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz",
"integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w=="
},
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/database-types": { "@firebase/database-types": {
@@ -75,17 +111,36 @@
} }
}, },
"@firebase/firestore": { "@firebase/firestore": {
"version": "1.5.2", "version": "1.5.3",
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.5.3.tgz",
<<<<<<< HEAD
"integrity": "sha512-CPYLvkGZBKE47oQC9a0q13UMVRj3LvnSbB1nOerktE3CGRHKy44LxDumamN8Kj067hV/80mKK9FdbeUufwO/Rg==", "integrity": "sha512-CPYLvkGZBKE47oQC9a0q13UMVRj3LvnSbB1nOerktE3CGRHKy44LxDumamN8Kj067hV/80mKK9FdbeUufwO/Rg==",
=======
"integrity": "sha512-O/yAbXpitOA6g627cUl0/FHYlkTy1EiEKMKOlnlMOJF2fH+nLVZREXjsrCC7N2tIvTn7yYwfpZ4zpSNvrhwiTA==",
>>>>>>> 7969d3b10bc35a9078834c5ee2ba8c8fd60d338f
"requires": { "requires": {
"@firebase/firestore-types": "1.5.0", "@firebase/firestore-types": "1.5.0",
"@firebase/logger": "0.1.24", "@firebase/logger": "0.1.25",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"@firebase/webchannel-wrapper": "0.2.26", "@firebase/webchannel-wrapper": "0.2.26",
"@grpc/proto-loader": "^0.5.0", "@grpc/proto-loader": "^0.5.0",
"grpc": "1.23.3", "grpc": "1.23.3",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/logger": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz",
"integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w=="
},
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/firestore-types": { "@firebase/firestore-types": {
@@ -94,9 +149,9 @@
"integrity": "sha512-VhRHNbEbak+R2iK8e1ir2Lec7eaHMZpGTRy6LMtzATYthlkwNHF9tO8JU8l6d1/kYkI4+DWzX++i3HhTziHEWA==" "integrity": "sha512-VhRHNbEbak+R2iK8e1ir2Lec7eaHMZpGTRy6LMtzATYthlkwNHF9tO8JU8l6d1/kYkI4+DWzX++i3HhTziHEWA=="
}, },
"@firebase/functions": { "@firebase/functions": {
"version": "0.4.17", "version": "0.4.18",
"resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.17.tgz", "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.18.tgz",
"integrity": "sha512-heWMXrR3hgvQNe1JEZMUeY7a0QFLMVwVS+lzLq/lzk06bj22X9bJy7Yct+/P9P1ftnsCGLrhk3jAEuL78seoqg==", "integrity": "sha512-N/ijwpxJy26kOErYIi5QS8pQgMZEuEMF/zDaNmgqcoN3J8P52NhBnVQZnIl+U4W96nQfNiURhSwXEERHFyvSZQ==",
"requires": { "requires": {
"@firebase/functions-types": "0.3.8", "@firebase/functions-types": "0.3.8",
"@firebase/messaging-types": "0.3.2", "@firebase/messaging-types": "0.3.2",
@@ -110,14 +165,24 @@
"integrity": "sha512-9hajHxA4UWVCGFmoL8PBYHpamE3JTNjObieMmnvZw3cMRTP2EwipMpzZi+GPbMlA/9swF9yHCY/XFAEkwbvdgQ==" "integrity": "sha512-9hajHxA4UWVCGFmoL8PBYHpamE3JTNjObieMmnvZw3cMRTP2EwipMpzZi+GPbMlA/9swF9yHCY/XFAEkwbvdgQ=="
}, },
"@firebase/installations": { "@firebase/installations": {
"version": "0.2.6", "version": "0.2.7",
"resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.2.6.tgz", "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.2.7.tgz",
"integrity": "sha512-hkuKmBtnsmqIfWxt9KyaN+cP574pfTcB81IG5tnmVcgP1xQ4hyQ9LRP0M7jDTGWMw272TInBzUuaM05xw9GMnA==", "integrity": "sha512-67tzowHVwRBtEuB1HLMD+fCdoRyinOQlMKBes7UwrtZIVd0CPDUqAKxNqup5EypWZb7O2tqFtRzK7POajfSNMA==",
"requires": { "requires": {
"@firebase/installations-types": "0.1.2", "@firebase/installations-types": "0.1.2",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"idb": "3.0.2", "idb": "3.0.2",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/installations-types": { "@firebase/installations-types": {
@@ -125,19 +190,24 @@
"resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.1.2.tgz", "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.1.2.tgz",
"integrity": "sha512-fQaWIW8hyX1XUN7+FCSPjvM1agFjGidVuF4Sxi7aFwfyh5t+4fD2VpM4wCQbWmodnx4fZLvsuQd9mkxxU+lGYQ==" "integrity": "sha512-fQaWIW8hyX1XUN7+FCSPjvM1agFjGidVuF4Sxi7aFwfyh5t+4fD2VpM4wCQbWmodnx4fZLvsuQd9mkxxU+lGYQ=="
}, },
"@firebase/logger": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.24.tgz",
"integrity": "sha512-wPwhWCepEjWiTIqeC9U+7Hcw4XwezKPdXmyXbYSPiWNDcVekNgMPkntwSK+/2ufJO/1nMwAL2n6fL12oQG/PpQ=="
},
"@firebase/messaging": { "@firebase/messaging": {
"version": "0.4.10", "version": "0.4.11",
"resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.4.10.tgz", "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.4.11.tgz",
"integrity": "sha512-WqtSqlulV2ix4MZ3r1HwGAEj0DiEWtpNCSPh5wOXZsj8Kd01Q2QPTLUtUWmwXSV9WCQWnowfE2x8wjq5388ixw==", "integrity": "sha512-KYt479yio6ThkV7Pb9LRB1KPIBio+OR4RozwyoLC1ZSVQdTIrd/sVEuDSzYY88Wh/6Kg6ejdu2z6mfWG9l1ZaQ==",
"requires": { "requires": {
"@firebase/messaging-types": "0.3.2", "@firebase/messaging-types": "0.3.2",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/messaging-types": { "@firebase/messaging-types": {
@@ -146,15 +216,30 @@
"integrity": "sha512-2qa2qNKqpalmtwaUV3+wQqfCm5myP/dViIBv+pXF8HinemIfO1IPQtr9pCNfsSYyus78qEhtfldnPWXxUH5v0w==" "integrity": "sha512-2qa2qNKqpalmtwaUV3+wQqfCm5myP/dViIBv+pXF8HinemIfO1IPQtr9pCNfsSYyus78qEhtfldnPWXxUH5v0w=="
}, },
"@firebase/performance": { "@firebase/performance": {
"version": "0.2.18", "version": "0.2.19",
"resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.2.18.tgz", "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.2.19.tgz",
"integrity": "sha512-PcN+nTPaMGqODfwAXgwbaCvcxXH+YzvK6UpZzm0Bl9wmW28/oJipnUxF3cYbVGCiaLAaByIPVSIF22XhTOjUtA==", "integrity": "sha512-dINWwR/XcSiSnFNNX7QWfec8bymiXk1Zp6mPyPN+R9ONMrpDbygQUy06oT/6r/xx9nHG4Za6KMUJag3sWNKqnQ==",
"requires": { "requires": {
"@firebase/installations": "0.2.6", "@firebase/installations": "0.2.7",
"@firebase/logger": "0.1.24", "@firebase/logger": "0.1.25",
"@firebase/performance-types": "0.0.3", "@firebase/performance-types": "0.0.3",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/logger": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz",
"integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w=="
},
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/performance-types": { "@firebase/performance-types": {
@@ -163,9 +248,9 @@
"integrity": "sha512-RuC63nYJPJU65AsrNMc3fTRcRgHiyNcQLh9ufeKUT1mEsFgpxr167gMb+tpzNU4jsbvM6+c6nQAFdHpqcGkRlQ==" "integrity": "sha512-RuC63nYJPJU65AsrNMc3fTRcRgHiyNcQLh9ufeKUT1mEsFgpxr167gMb+tpzNU4jsbvM6+c6nQAFdHpqcGkRlQ=="
}, },
"@firebase/polyfill": { "@firebase/polyfill": {
"version": "0.3.21", "version": "0.3.22",
"resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.21.tgz", "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.22.tgz",
"integrity": "sha512-2mqS3FQHMhCGyfMGRsaZEypHSBD8hVmp9ZBnZSkn8hq5sSOLiNTFSC0FsvNu5z99GNsPQJFTui8bxcZl5cHQbw==", "integrity": "sha512-PYbEqDHJhJJoF2Q5IB/oP0Tz6O2vSUPtODy9kUQibi+T0bK1gkTaySPwz8GAgHfIpFNENj1kK+7Xpf87R8bYbw==",
"requires": { "requires": {
"core-js": "3.2.1", "core-js": "3.2.1",
"promise-polyfill": "8.1.3", "promise-polyfill": "8.1.3",
@@ -180,13 +265,23 @@
} }
}, },
"@firebase/storage": { "@firebase/storage": {
"version": "0.3.11", "version": "0.3.12",
"resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.11.tgz", "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.12.tgz",
"integrity": "sha512-Q2ffXE+X62gFy5mZkg7qhzAC7+kqaNZWpgS+297h/hWr/cFBDyC8eBPmnI509eKi2okixmOMbWnNluZkNYNSfw==", "integrity": "sha512-8hXt3qPZlVH+yPF4W9Dc15/gBiTPGUJUgYs3dH9WnO41QWl1o4aNlZpZK/pdnpCIO1GmN0+PxJW9TCNb0H0Hqw==",
"requires": { "requires": {
"@firebase/storage-types": "0.3.3", "@firebase/storage-types": "0.3.3",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"tslib": "1.10.0" "tslib": "1.10.0"
},
"dependencies": {
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
}
} }
}, },
"@firebase/storage-types": { "@firebase/storage-types": {
@@ -194,23 +289,15 @@
"resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.3.tgz",
"integrity": "sha512-fUp4kpbxwDiWs/aIBJqBvXgFHZvgoND2JA0gJYSEsXtWtVwfgzY/710plErgZDeQKopX5eOR1sHskZkQUy0U6w==" "integrity": "sha512-fUp4kpbxwDiWs/aIBJqBvXgFHZvgoND2JA0gJYSEsXtWtVwfgzY/710plErgZDeQKopX5eOR1sHskZkQUy0U6w=="
}, },
"@firebase/util": {
"version": "0.2.27",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.27.tgz",
"integrity": "sha512-kFlbWNX1OuLfHrDXZ5QLmNNiLtMyxzbBgMo1DY1tXMjKK1AMYsHnyjInA8esvO0SCDp5XN3Pt9EDlhY4sRiLsw==",
"requires": {
"tslib": "1.10.0"
}
},
"@firebase/webchannel-wrapper": { "@firebase/webchannel-wrapper": {
"version": "0.2.26", "version": "0.2.26",
"resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.26.tgz", "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.26.tgz",
"integrity": "sha512-VlTurkvs4v7EVFWESBZGOPghFEokQhU5au5CP9WqA8B2/PcQRDsaaQlQCA6VATuEnW+vtSiSBvTiOc4004f8xg==" "integrity": "sha512-VlTurkvs4v7EVFWESBZGOPghFEokQhU5au5CP9WqA8B2/PcQRDsaaQlQCA6VATuEnW+vtSiSBvTiOc4004f8xg=="
}, },
"@google-cloud/common": { "@google-cloud/common": {
"version": "2.2.0", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.2.tgz",
"integrity": "sha512-ArSNbbuMOWVhrSasxECEYRcjMzkPgTfXJHQE5gccyDaoBv0oKqG9S2lse2KAgHpRADRna7wKiX9PWOpeB19VvA==", "integrity": "sha512-AgMdDgLeYlEG17tXtMCowE7mplm907pcugtfJYYAp06HNe9RDnunUIY5KMnn9yikYl7NXNofARC+hwG77Zsa4g==",
"optional": true, "optional": true,
"requires": { "requires": {
"@google-cloud/projectify": "^1.0.0", "@google-cloud/projectify": "^1.0.0",
@@ -260,9 +347,9 @@
"optional": true "optional": true
}, },
"@google-cloud/storage": { "@google-cloud/storage": {
"version": "3.2.1", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-3.2.1.tgz", "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-3.3.0.tgz",
"integrity": "sha512-129EwPGej6bXzY1u5nja2aeMDew6DIHaJn7ZV6nteQ74LQQSNv2jKrqTlyhndBsAwpuwQAxeghPTCoFT/H8Frg==", "integrity": "sha512-9jmHJ0ncQTcrZRwq5MRjXEwuCFkIjHenYwVbycV6bbZ4O84Hcgg4Yp33sKcJug5rvZeVgrpCzPbYXqO3B0LzJw==",
"optional": true, "optional": true,
"requires": { "requires": {
"@google-cloud/common": "^2.1.1", "@google-cloud/common": "^2.1.1",
@@ -271,27 +358,50 @@
"arrify": "^2.0.0", "arrify": "^2.0.0",
"compressible": "^2.0.12", "compressible": "^2.0.12",
"concat-stream": "^2.0.0", "concat-stream": "^2.0.0",
"date-and-time": "^0.9.0", "date-and-time": "^0.10.0",
"duplexify": "^3.5.0", "duplexify": "^3.5.0",
"extend": "^3.0.2", "extend": "^3.0.2",
"gaxios": "^2.0.1", "gaxios": "^2.0.1",
"gcs-resumable-upload": "^2.0.0", "gcs-resumable-upload": "^2.2.4",
"hash-stream-validation": "^0.2.1", "hash-stream-validation": "^0.2.1",
"mime": "^2.2.0", "mime": "^2.2.0",
"mime-types": "^2.0.8", "mime-types": "^2.0.8",
"onetime": "^5.1.0", "onetime": "^5.1.0",
"p-limit": "^2.2.0", "p-limit": "^2.2.0",
"pumpify": "^2.0.0", "pumpify": "^2.0.0",
"readable-stream": "^3.4.0",
"snakeize": "^0.1.0", "snakeize": "^0.1.0",
"stream-events": "^1.0.1", "stream-events": "^1.0.1",
"through2": "^3.0.0", "through2": "^3.0.0",
"xdg-basedir": "^4.0.0" "xdg-basedir": "^4.0.0"
},
"dependencies": {
"readable-stream": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
"integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
"optional": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"optional": true,
"requires": {
"safe-buffer": "~5.2.0"
}
}
} }
}, },
"@grpc/grpc-js": { "@grpc/grpc-js": {
"version": "0.5.3", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.5.3.tgz", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.5.4.tgz",
"integrity": "sha512-doDzxjdN0IJihQJvjDkZun9bZp/TW2EKO5E4fNvw8634kU1eNqPnFtAmiEiIYptqJ9StC+zRo1mwrazhqI0k5A==", "integrity": "sha512-aY4fTCz7jq7oKFmfAeZVqGzMCR5I9NLdY9E2fJ70QtGXwlJnTaN6cnbRmCk23/aKPx9UHqOtk2lyjpN6LbAaxw==",
"optional": true, "optional": true,
"requires": { "requires": {
"semver": "^6.2.0" "semver": "^6.2.0"
@@ -500,7 +610,7 @@
"ansi-regex": { "ansi-regex": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true "dev": true
}, },
"ansi-styles": { "ansi-styles": {
@@ -547,6 +657,15 @@
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"dev": true "dev": true
}, },
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
}
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -711,28 +830,28 @@
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
}
} }
}, },
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
}
}
},
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -899,9 +1018,9 @@
"optional": true "optional": true
}, },
"date-and-time": { "date-and-time": {
"version": "0.9.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.9.0.tgz", "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.10.0.tgz",
"integrity": "sha512-4JybB6PbR+EebpFx/KyR5Ybl+TcdXMLIJkyYsCx3P4M4CWGMuDyFF19yh6TyasMAIF5lrsgIxiSHBXh2FFc7Fg==", "integrity": "sha512-IbIzxtvK80JZOVsWF6+NOjunTaoFVYxkAQoyzmflJyuRCJAJebehy48mPiCAedcGp4P7/UO3QYRWa0fe6INftg==",
"optional": true "optional": true
}, },
"debug": { "debug": {
@@ -1071,9 +1190,15 @@
} }
}, },
"end-of-stream": { "end-of-stream": {
<<<<<<< HEAD
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
=======
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.3.tgz",
"integrity": "sha512-cbNhPFS6MlYlWTGncSiDYbdqKhwWFy7kNeb1YSOG6K65i/wPTkLVCJQj0hXA4j0m5Da+hBWnqopEnu1FFelisQ==",
>>>>>>> 7969d3b10bc35a9078834c5ee2ba8c8fd60d338f
"optional": true, "optional": true,
"requires": { "requires": {
"once": "^1.4.0" "once": "^1.4.0"
@@ -1415,42 +1540,55 @@
} }
}, },
"firebase": { "firebase": {
"version": "6.6.1", "version": "6.6.2",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-6.6.1.tgz", "resolved": "https://registry.npmjs.org/firebase/-/firebase-6.6.2.tgz",
"integrity": "sha512-iXbHPIBRt04xYSjWffnARqZbc3vUc0RTnOHsMtAqaT7pqDWicaghEwj2WbCJ0+JLAiKnLNK7fTjW73zfKQSSoQ==", "integrity": "sha512-uL9uNbutC0T8GAxrGgOCC35Ven3QKJqzJozNoVIpBuiWrB9ifm9aKOxn44h6o5ouviax3LVvoiG2jLkLkdQq4A==",
"requires": { "requires": {
"@firebase/app": "0.4.16", "@firebase/app": "0.4.17",
"@firebase/app-types": "0.4.3", "@firebase/app-types": "0.4.3",
"@firebase/auth": "0.12.0", "@firebase/auth": "0.12.0",
"@firebase/database": "0.5.3", "@firebase/database": "0.5.4",
"@firebase/firestore": "1.5.2", "@firebase/firestore": "1.5.3",
"@firebase/functions": "0.4.17", "@firebase/functions": "0.4.18",
"@firebase/installations": "0.2.6", "@firebase/installations": "0.2.7",
"@firebase/messaging": "0.4.10", "@firebase/messaging": "0.4.11",
"@firebase/performance": "0.2.18", "@firebase/performance": "0.2.19",
"@firebase/polyfill": "0.3.21", "@firebase/polyfill": "0.3.22",
"@firebase/storage": "0.3.11", "@firebase/storage": "0.3.12",
"@firebase/util": "0.2.27" "@firebase/util": "0.2.28"
}, },
"dependencies": { "dependencies": {
"@firebase/database": { "@firebase/database": {
"version": "0.5.3", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.3.tgz", "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.4.tgz",
"integrity": "sha512-TFjQ/M0T4jO24jAMU5cZAHNk3ndNfeNtGKe5PL4o/YrGYJHg3XaE2LKzU/vFrXUFLnLxqbETzXjFa4hTA6cDUg==", "integrity": "sha512-Hz1Bi3fzIcNNocE4EhvvwoEQGurG2BGssWD3/6a2bzty+K1e57SLea2Ied8QYNBUU1zt/4McHfa3Y71EQIyn/w==",
"requires": { "requires": {
"@firebase/database-types": "0.4.3", "@firebase/database-types": "0.4.3",
"@firebase/logger": "0.1.24", "@firebase/logger": "0.1.25",
"@firebase/util": "0.2.27", "@firebase/util": "0.2.28",
"faye-websocket": "0.11.3", "faye-websocket": "0.11.3",
"tslib": "1.10.0" "tslib": "1.10.0"
} }
},
"@firebase/logger": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz",
"integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w=="
},
"@firebase/util": {
"version": "0.2.28",
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz",
"integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==",
"requires": {
"tslib": "1.10.0"
}
} }
} }
}, },
"firebase-admin": { "firebase-admin": {
"version": "8.5.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.5.0.tgz", "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.6.0.tgz",
"integrity": "sha512-rvgCj5Z1iFOT6K6uW37VRl4PKNpAcBFu/FIQ4Nl5bFnqbHSxf+QxzsqdsUtIxdqZU1yh2DTs2t+s5qORx/T9+g==", "integrity": "sha512-+JqOinU5bYUkg434LqEBXrHMrIBhL/+HwWEgbZpS1sBKHQRJK7LlcBrayqxvQKwJzgh5xs/JTInTmkozXk7h1w==",
"requires": { "requires": {
"@firebase/database": "^0.5.1", "@firebase/database": "^0.5.1",
"@google-cloud/firestore": "^2.0.0", "@google-cloud/firestore": "^2.0.0",
@@ -1524,6 +1662,29 @@
"integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
"dev": true "dev": true
}, },
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -1549,7 +1710,8 @@
"functional-red-black-tree": { "functional-red-black-tree": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"optional": true
}, },
"gaxios": { "gaxios": {
"version": "2.0.1", "version": "2.0.1",
@@ -1564,9 +1726,9 @@
} }
}, },
"gcp-metadata": { "gcp-metadata": {
"version": "2.0.2", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-2.0.2.tgz", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.0.0.tgz",
"integrity": "sha512-dxPXBvjyfz5qFEBXzEwNmuZXwsGYfuASGYeg3CKZDaQRXdiWti9J3/Ezmtyon1OrCNpDO2YekyoSjEqMtsrcXw==", "integrity": "sha512-WP5/TZWri9TrD41jNr8ukY9dKYLL+8jwQVwbtUbmprjWuyybdnJNkbXbwqD2sdbXIVXD1WCqzfj7QftSLB6K8Q==",
"optional": true, "optional": true,
"requires": { "requires": {
"gaxios": "^2.0.1", "gaxios": "^2.0.1",
@@ -1608,25 +1770,25 @@
"dev": true "dev": true
}, },
"google-auth-library": { "google-auth-library": {
"version": "5.2.1", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.2.1.tgz", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.2.2.tgz",
"integrity": "sha512-p9vO6UcRIK/zD3PxoMijaUfFYu6tvzaQwvag1K/82O42NBeAnmllyQUgqaBhcAh9FzFAVlN4bQIaO8+prpE7Vg==", "integrity": "sha512-0vzniXbjD5SE9aenAMqhjVR99wvqLpyd5Fw6zC3WxJ15GIMGx96tq+Cu1WRviqsnQqhrmnad6T69kv6qkj/w2Q==",
"optional": true, "optional": true,
"requires": { "requires": {
"arrify": "^2.0.0", "arrify": "^2.0.0",
"base64-js": "^1.3.0", "base64-js": "^1.3.0",
"fast-text-encoding": "^1.0.0", "fast-text-encoding": "^1.0.0",
"gaxios": "^2.0.0", "gaxios": "^2.0.0",
"gcp-metadata": "^2.0.0", "gcp-metadata": "^3.0.0",
"gtoken": "^4.0.0", "gtoken": "^4.0.0",
"jws": "^3.1.5", "jws": "^3.1.5",
"lru-cache": "^5.0.0" "lru-cache": "^5.0.0"
} }
}, },
"google-gax": { "google-gax": {
"version": "1.5.2", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.5.2.tgz", "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.6.1.tgz",
"integrity": "sha512-NceyDzlw4mQz6qH3bDIuRtfDAZKehM96QpnPPJ3Hur7FA/gPzpzboUYwhfP6q5obSP4LuSSDhI/76Fu51/ljtg==", "integrity": "sha512-5/6uaUA9qAqRKVe2sjvMgsnU/HbfQisQTM5EZ5DfNGOYVBoTsPBdOhR2ZqEWPyqHe7YkdzVHev3FH9W3YWcORw==",
"optional": true, "optional": true,
"requires": { "requires": {
"@grpc/grpc-js": "^0.5.2", "@grpc/grpc-js": "^0.5.2",
@@ -2097,9 +2259,15 @@
} }
}, },
"gtoken": { "gtoken": {
<<<<<<< HEAD
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.0.0.tgz", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.0.0.tgz",
"integrity": "sha512-XaRCfHJxhj06LmnWNBzVTAr85NfAErq0W1oabkdqwbq3uL/QTB1kyvGog361Uu2FMG/8e3115sIy/97Rnd4GjQ==", "integrity": "sha512-XaRCfHJxhj06LmnWNBzVTAr85NfAErq0W1oabkdqwbq3uL/QTB1kyvGog361Uu2FMG/8e3115sIy/97Rnd4GjQ==",
=======
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.0.tgz",
"integrity": "sha512-wqyn2gf5buzEZN4QNmmiiW2i2JkEdZnL7Z/9p44RtZqgt4077m4khRgAYNuu8cBwHWCc6MsP6eDUn/KkF6jFIw==",
>>>>>>> 7969d3b10bc35a9078834c5ee2ba8c8fd60d338f
"optional": true, "optional": true,
"requires": { "requires": {
"gaxios": "^2.0.0", "gaxios": "^2.0.0",
@@ -2273,7 +2441,8 @@
"imurmurhash": { "imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"optional": true
}, },
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
@@ -2288,7 +2457,8 @@
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"optional": true
}, },
"inquirer": { "inquirer": {
"version": "6.5.2", "version": "6.5.2",
@@ -2344,6 +2514,11 @@
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
"optional": true "optional": true
}, },
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
},
"is-date-object": { "is-date-object": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
@@ -2353,8 +2528,10 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true "requires": {
"number-is-nan": "^1.0.0"
}
}, },
"is-obj": { "is-obj": {
"version": "2.0.0", "version": "2.0.0",
@@ -2752,6 +2929,7 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -3196,7 +3374,8 @@
"signal-exit": { "signal-exit": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true
}, },
"slice-ansi": { "slice-ansi": {
"version": "2.1.0", "version": "2.1.0",
@@ -3249,9 +3428,9 @@
"string-width": { "string-width": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": { "requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0", "is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0" "strip-ansi": "^4.0.0"
} }
@@ -3265,7 +3444,7 @@
"strip-ansi": { "strip-ansi": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-regex": "^3.0.0" "ansi-regex": "^3.0.0"
@@ -3576,7 +3755,8 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
}, },
"write": { "write": {
"version": "1.0.3", "version": "1.0.3",

View File

@@ -10,11 +10,12 @@
"logs": "firebase functions:log" "logs": "firebase functions:log"
}, },
"engines": { "engines": {
"node": "8" "node": "10"
}, },
"dependencies": { "dependencies": {
"firebase": "^6.6.1", "axios": "^0.19.0",
"firebase-admin": "^8.0.0", "firebase": "^6.6.2",
"firebase-admin": "^8.6.0",
"firebase-functions": "^3.1.0" "firebase-functions": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {

6
functions/util/admin.js Normal file
View File

@@ -0,0 +1,6 @@
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
module.exports = { admin, db };

9
functions/util/config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
apiKey: "AIzaSyCvsWetg4qFdsPGfJ3LCw_QaaYzoan7Q34",
authDomain: "twistter-e4649.firebaseapp.com",
databaseURL: "https://twistter-e4649.firebaseio.com",
projectId: "twistter-e4649",
storageBucket: "twistter-e4649.appspot.com",
messagingSenderId: "20131817365",
appId: "1:20131817365:web:633c95fb08b16d4526b89c"
};

34
functions/util/fbAuth.js Normal file
View File

@@ -0,0 +1,34 @@
const { admin, db } = require('./admin');
// Acts as a middleman between the client and any function that you use it with
// The function will only execute if the user is logged in, or rather, they have
// a valid token
module.exports = (req, res, next) => {
let idToken;
// Checking that the token exists in the header of the request
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
idToken = req.headers.authorization.split('Bearer ')[1];
} else {
console.error('No token found');
return res.status(403).json({ error: 'Unauthorized'});
}
// Checking that the token is valid in firebase
admin.auth().verifyIdToken(idToken)
.then((decodedToken) => {
req.user = decodedToken;
return db.collection('users').where('userId', '==', req.user.uid)
.limit(1)
.get();
})
.then((data) => {
req.user.handle = data.docs[0].data().handle; // Save username
req.user.imageUrl = data.docs[0].data().imageUrl;
return next();
})
.catch((err) => {
console.error('Error while verifying token ', err);
return res.status(403).json(err);
});
};

View File

@@ -0,0 +1,36 @@
const isEmail = (str) => {
const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (str.match(emailRegEx)) return true;
else return false;
};
const isEmpty = (str) => {
if (str.trim() === "") return true;
else return false;
};
exports.validateUpdateProfileInfo = (data) => {
let errors = {};
let profileData = {};
// ?: Should users be able to change their handles and emails?
// Only adds the key to the database if the values are not empty
if (!isEmpty(data.firstName)) profileData.firstName = data.firstName.trim();
if (!isEmpty(data.lastName)) profileData.lastName = data.lastName.trim();
if (!isEmpty(data.bio)) profileData.bio = data.bio.trim();
if (isEmpty(data.email)) {
errors.email = "Must not be empty.";
} else if (!isEmail(data.email)) {
errors.email = "Must be a valid email.";
} else {
profileData.email = data.email;
}
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false,
profileData
};
};

View File

@@ -1,78 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Firebase Hosting</title>
<!-- update the version number as needed -->
<script defer src="/__/firebase/6.6.0/firebase-app.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/6.6.0/firebase-auth.js"></script>
<script defer src="/__/firebase/6.6.0/firebase-database.js"></script>
<script defer src="/__/firebase/6.6.0/firebase-messaging.js"></script>
<script defer src="/__/firebase/6.6.0/firebase-storage.js"></script>
<!-- initialize the SDK after all desired features are loaded -->
<script defer src="/__/firebase/init.js"></script>
<style media="screen">
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px; border-radius: 3px; }
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
@media (max-width: 600px) {
body, #message { margin-top: 0; background: white; box-shadow: none; }
body { border-top: 16px solid #ffa100; }
}
</style>
</head>
<body>
<div id="message">
<h2>This is a test message!</h2>
<h2>Welcome</h2>
<h1>Firebase Hosting Setup Complete</h1>
<p>You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!</p>
<a target="_blank" href="https://firebase.google.com/docs/hosting/">Open Hosting Documentation</a>
<div id="like_button_container"></div>
</div>
<p id="load">Firebase SDK Loading&hellip;</p>
<script>
document.addEventListener('DOMContentLoaded', function() {
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
// // The Firebase SDK is initialized and available here!
//
// firebase.auth().onAuthStateChanged(user => { });
// firebase.database().ref('/path/to/ref').on('value', snapshot => { });
// firebase.messaging().requestPermission().then(() => { });
// firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
//
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
try {
let app = firebase.app();
let features = ['auth', 'database', 'messaging', 'storage'].filter(feature => typeof app[feature] === 'function');
document.getElementById('load').innerHTML = `Firebase SDK loaded with ${features.join(', ')}`;
} catch (e) {
console.error(e);
document.getElementById('load').innerHTML = 'Error loading the Firebase SDK, check the console.';
}
});
</script>
<!-- Load React. -->
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<!-- Load our React component. -->
<script src="like_button.js"></script>
<script src="loader.js"></script>
</body>
</html>

View File

@@ -1,30 +0,0 @@
'use strict';
const e = React.createElement;
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {
liked: false
};
}
render() {
if (this.state.liked) {
return 'You liked this.';
}
return e(
'button', {
onClick: () => this.setState({
liked: true
})
},
'Like'
);
}
}
const domContainer = document.querySelector('#like_button_container');
ReactDOM.render(e(LikeButton), domContainer);

File diff suppressed because it is too large Load Diff

View File

@@ -3,10 +3,21 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.4.3",
"@material-ui/styles": "^4.5.0",
"@material-ui/system": "^4.5.0",
"axios": "^0.19.0",
"clsx": "^1.0.4",
"create-react-app": "^3.1.2",
"install": "^0.13.0",
"node-pre-gyp": "^0.13.0",
"react": "^16.9.0", "react": "^16.9.0",
"react-dom": "^16.9.0", "react-dom": "^16.9.0",
"react-router-dom": "^5.0.1", "react-redux": "^7.1.1",
"react-scripts": "0.9.5" "react-router-dom": "^5.1.0",
"react-scripts": "0.9.5",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0"
}, },
"devDependencies": {}, "devDependencies": {},
"scripts": { "scripts": {
@@ -14,5 +25,18 @@
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject" "eject": "react-scripts eject"
} },
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "https://us-central1-twistter-e4649.cloudfunctions.net/api"
} }

View File

@@ -5,6 +5,13 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Twistter</title> <title>Twistter</title>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
</style>
</head> </head>
<body> <body>

View File

@@ -0,0 +1,18 @@
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import userReducer from './reducers/userReducer';
import dataReducer from './reducers/dataReducer';
import uiReducer from './reducers/uiReducer';
const initialState = {};
const middleware = {thunk};
const reducers = combineReducers({
user: userReducer,
data: dataReducer,
UI: uiReducer
});
//const store = createStore(reducers, )

View File

View File

@@ -40,4 +40,11 @@
height:40px; height:40px;
border: 0px; border: 0px;
padding: 10px; padding: 10px;
} }
.container {
margin: 80px auto 0 auto;
max-width: 1200px;
color: #1da1f2;
}

View File

@@ -1,175 +1,48 @@
/* eslint-disable */
import React, { Component } from 'react'; import React, { Component } from 'react';
import logo from './twistter-logo.png';
import './App.css';
import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route'; import Route from 'react-router-dom/Route';
import './App.css'; import Navbar from './components/layout/NavBar';
import themeObject from './util/theme';
var validEmail = true; import home from './pages/Home';
var validUsername = false; import register from './pages/Register';
var validPassword = false; import login from './pages/Login';
var passwordsMatch = false; import user from './pages/user';
const emailBlur = () => { import writeMicroblog from './Writing_Microblogs.js';
//var email = document.getElementById("email"); import edit from './pages/edit.js';
import userLine from './Userline.js';
/*if() { const theme = createMuiTheme(themeObject);
validEmail = true;
}
else {
validEmail = false;
alert("Email is invalid.");
}*/
if(validEmail && validUsername && validPassword && passwordsMatch) {
document.getElementById("submit").disabled = false;
}
else {
document.getElementById("submit").disabled = true;
}
}
const usernameBlur = () => {
var username = document.getElementById("username");
if(username.value.length >= 3 && username.value.length <= 50) {
validUsername = true;
}
else {
validUsername = false;
alert("Username must be between 3 and 50 characters long.");
}
if(validEmail && validUsername && validPassword && passwordsMatch) {
document.getElementById("submit").disabled = false;
}
else {
document.getElementById("submit").disabled = true;
}
}
const passwordBlur = () => {
var password = document.getElementById("password");
if(password.value.length >= 8 && password.value.length <= 20) {
validPassword = true;
}
else {
validPassword = false;
alert("Password must be between 8 and 20 characters long.");
}
if(validEmail && validUsername && validPassword && passwordsMatch) {
document.getElementById("submit").disabled = false;
}
else {
document.getElementById("submit").disabled = true;
}
}
const confirmPasswordBlur = () => {
var password = document.getElementById("password");
var confirmPassword = document.getElementById("confirmPassword");
if(password.value === confirmPassword.value) {
passwordsMatch = true;
}
else {
passwordsMatch = false;
alert("Passwords must match.");
}
if(validEmail && validUsername && validPassword && passwordsMatch) {
document.getElementById("submit").disabled = false;
}
else {
document.getElementById("submit").disabled = true;
}
}
class App extends Component { class App extends Component {
render() { render() {
return ( return (
<Router> <MuiThemeProvider theme={theme}>
<div className="app"> <Router>
<div className='container' >
<Navbar />
<Route path="/" exact render={
() => {
return (
<div>
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Welcome to Twistter!</b>
<br/><br/>
<b>See the most interesting topics people are following right now.</b>
</div>
<br/><br/><br/><br/>
<div>
<b>Join today or sign in if you already have an account.</b>
<br/><br/>
<button class="authButtons register"><a href="/register">Sign up</a></button>
<br/><br/>
<button class="authButtons login"><a href="/login">Sign in</a></button>
</div>
</div>
)
}
}/>
<Route path="/register" exact render={
() => {
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Create your account</b>
<br/><br/>
<input class="authInput" id="email" placeholder="Email" onBlur={() => emailBlur()}></input>
<br/><br/>
<input class="authInput" id="username" placeholder="Username" onBlur={() => usernameBlur()}></input>
<br/><br/>
<input class="authInput" id="password" placeholder="Password" onBlur={() => passwordBlur()}></input>
<br/><br/>
<input class="authInput" id="confirmPassword" placeholder="Confirm Password" onBlur={() => confirmPasswordBlur()}></input>
<br/><br/>
<button class="authButtons register" id="submit" onclick="" disabled>Sign up</button>
</div>
)
}
}/>
<Route path="/login" exact render={
() => {
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Log in to Twistter</b>
<br/><br/>
<input class="authInput" placeholder="Username or email"></input>
<br/><br/>
<input class="authInput" placeholder="Password"></input>
<br/><br/>
<button class="authButtons register" onclick="">Sign in</button>
</div>
)
}
}/>
</div> </div>
<div className="app">
<Route exact path="/" component={home}/>
<Route exact path="/register" component={register}/>
<Route exact path="/login" component={login}/>
<Route exact path="/user" component={user}/>
<Route exact path="/home" component={writeMicroblog}/>
<Route exact path="/edit" component={edit}/>
<Route exact path="/user" component={userLine}/>
</div>
</Router> </Router>
</MuiThemeProvider>
); );
} }
} }
export default App; export default App;

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
import React, { Component} from 'react';
import './App.css';
import logo from './images/twistter-logo.png';
import TextField from '@material-ui/core/TextField';
class Logout extends Component {
render() {
return(
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Logout of your Twistter Account</b>
<br/><br/>
<br/><br/>
<button className="authButtons register" type="submit">Sign Out</button>
</div>
);
};
}
export default Logout;

View File

@@ -0,0 +1,64 @@
import React, { Component } from "react";
import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route';
import axios from 'axios';
import Box from '@material-ui/core/Box'
import {borders} from '@material-ui/system';
import { sizing } from '@material-ui/system';
// var moment = require('moment');
class Userline extends Component {
constructor(props)
{
super(props);
this.state = {
microBlogs : [],
}
}
componentDidMount() {
axios.get('http://localhost:5001/twistter-e4649/us-central1/api/getallPostsforUser')
.then(res => {
const post = res.data;
this.setState({microBlogs : post})
})
}
render() {
const sortedPosts = (this.state.microBlogs).sort((a,b) =>
-a.createdAt.localeCompare(b.createdAt)
)
return (
<div>
<div style={{fontsize: "13px", textAlign: "left", marginLeft: "14px"}}>
<p>Userline</p>
</div>
<Box border={1} width="25%" flex="1" height="auto" m={2} fontSize="13px" textAlign= "left" padding="5px" flexWrap= "wrap" flexDirection= "row" >
<div style={{flexWrap: "wrap", flex: "1", flexDirection: "row", wordBreak: "break-word"}}>
<p>
{sortedPosts.map((microBlog) => <p>Microblog Title: {microBlog.microBlogTitle}
<br></br>When post was created: {microBlog.createdAt.substring(0,10) +
" " + microBlog.createdAt.substring(11,19)}
<br></br>Number of comments: {microBlog.commentCount}
<br></br>Number of likes: {microBlog.likeCount}
<br></br>Body of post: {microBlog.body}
<br></br>Tagged topics: {microBlog.microBlogTopics.join("," + " ")}
</p>)}
</p>
</div>
</Box>
</div>
)
}
}
export default Userline;

View File

@@ -1,6 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route'; import Route from 'react-router-dom/Route';
import axios from 'axios';
class Writing_Microblogs extends Component { class Writing_Microblogs extends Component {
@@ -10,7 +11,8 @@ class Writing_Microblogs extends Component {
this.state = { this.state = {
value: '', value: '',
title: '', title: '',
characterCount: 10 topics: '',
characterCount: 250
}; };
@@ -18,15 +20,37 @@ class Writing_Microblogs extends Component {
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeforPost = this.handleChangeforPost.bind(this); this.handleChangeforPost = this.handleChangeforPost.bind(this);
this.handleChangeforTopics = this.handleChangeforTopics.bind(this);
} }
handleChange(event) { handleChange(event) {
this.setState( {title: event.target.value }); this.setState( {title: event.target.value });
} }
handleChangeforTopics(event) {
this.setState( {topics: event.target.value});
}
handleSubmit(event) { handleSubmit(event) {
alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value); // alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value);
const response = axios.post(
'http://localhost:5001/twistter-e4649/us-central1/api/putPost',
{ body: this.state.value,
userHandle: "new user",
userImage: "bing-url",
microBlogTitle: this.state.title,
microBlogTopics: this.state.topics.split(', ')
},
{ headers: { 'Content-Type': 'application/json'} }
)
console.log(response.data);
alert('Post was shared successfully!');
event.preventDefault(); event.preventDefault();
this.setState({value: '', title: '',characterCount: 250, topics: ''})
} }
handleChangeforPost(event) { handleChangeforPost(event) {
@@ -35,23 +59,29 @@ class Writing_Microblogs extends Component {
handleChangeforCharacterCount(event) { handleChangeforCharacterCount(event) {
const charCount = event.target.value.length const charCount = event.target.value.length
const charRemaining = 10 - charCount const charRemaining = 250 - charCount
this.setState({characterCount: charRemaining }) this.setState({characterCount: charRemaining })
} }
render() { render() {
return ( return (
<div> <div>
<div style={{ width: "200px", height: "50px", marginTop: "180px", marginLeft: "30px" }}> <div style={{ width: "200px", height: "50px", marginTop: "180px", marginLeft: "50px" }}>
<form> <form>
<input type="text" placeholder="Enter Microblog Title" value={this.state.title} onChange={this.handleChange} /> <textarea placeholder="Enter Microblog Title" value={this.state.title} required onChange={this.handleChange} cols={30} rows={1} />
</form>
</div>
<div style={{ width: "200px", height: "50px", marginLeft: "50px"}} >
<form>
<textarea placeholder="Enter topics seperated by a comma" value={this.state.topics} required onChange={this.handleChangeforTopics} cols={40} rows={1} />
</form> </form>
</div> </div>
<div style={{ width: "200px", marginLeft: "50px"}}> <div style={{ width: "200px", marginLeft: "50px"}}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<textarea value={this.state.value} maxLength="10" placeholder= "Write Microblog here..." <textarea value={this.state.value} required maxLength="250" placeholder= "Write Microblog here..."
onChange = { (e) => { this.handleChangeforPost(e); this.handleChangeforCharacterCount(e) } } cols={40} rows={20} /> onChange = { (e) => { this.handleChangeforPost(e); this.handleChangeforCharacterCount(e) } } cols={40} rows={20} />
<div style={{ fontSize: "14px", marginRight: "-100px"}} > <div style={{ fontSize: "14px", marginRight: "-100px"}} >
<p2>Characters Left: {this.state.characterCount}</p2> <p2>Characters Left: {this.state.characterCount}</p2>

View File

@@ -0,0 +1,31 @@
/* eslint-disable */
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import AppBar from '@material-ui/core/AppBar';
import ToolBar from '@material-ui/core/Toolbar';
import Button from '@material-ui/core/Button';
export class Navbar extends Component {
render() {
return (
<AppBar>
<ToolBar>
<Button component={ Link } to='/'>
Home
</Button>
<Button component={ Link } to='/login'>
Login
</Button>
<Button component={ Link } to='/register'>
Register
</Button>
<Button component={ Link } to='/logout'>
Logout
</Button>
</ToolBar>
</AppBar>
)
}
}
export default Navbar;

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Chip from '@material-ui/core/Chip';
import Paper from '@material-ui/core/Paper';
// TODO: fix the style
const styles = makeStyles(theme => ({
root: {
display: 'flex',
justifyContent: 'center',
flexWrap: 'wrap',
padding: theme.spacing(0.5),
},
chip: {
margin: theme.spacing(0.5),
},
}));
export default function ChipsArray() {
const classes = useStyles();
const [chipData, setChipData] = React.useState([
{ key: 0, label: 'Angular' },
{ key: 1, label: 'jQuery' },
{ key: 2, label: 'Polymer' },
{ key: 3, label: 'React' },
{ key: 4, label: 'Vue.js' },
]);
const handleDelete = chipToDelete => () => {
if (chipToDelete.label === 'React') {
alert('Why would you want to delete React?! :)');
return;
}
setChipData(chips => chips.filter(chip => chip.key !== chipToDelete.key));
};
return (
<Paper className={classes.root}>
{chipData.map(data => {
return (
<Chip
key={data.key}
label={data.label}
onDelete={handleDelete(data)}
className={classes.chip}
/>
);
})}
</Paper>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -1,5 +0,0 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from './App'; import App from './App';
import './index.css';
ReactDOM.render( ReactDOM.render(
<App />, <App />,

View File

@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import '../App.css';
import logo from '../images/twistter-logo.png';
class Home extends Component {
render() {
return (
<div>
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Welcome to Twistter!</b>
<br/><br/>
<b>See the most interesting topics people are following right now.</b>
</div>
<br/><br/><br/><br/>
<div>
<b>Join today or sign in if you already have an account.</b>
<br/><br/>
<form action="./register">
<button className="authButtons register">Sign up</button>
</form>
<br/>
<form action="./login">
<button className="authButtons login">Sign in</button>
</form>
</div>
</div>
);
}
}
export default Home;

View File

@@ -0,0 +1,73 @@
/* eslint-disable */
import React, { Component } from 'react';
import '../App.css';
import axios from 'axios';
import PropTypes from 'prop-types';
import logo from '../images/twistter-logo.png';
import TextField from '@material-ui/core/TextField';
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
errors: {}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleSubmit = (event) => {
event.preventDefault();
const userData = {
email: this.state.email,
password: this.state.password
};
axios.post('http://localhost:5001/twistter-e4649/us-central1/api/login', userData)
.then(res => {
console.log(res.data);
localStorage.setItem('firebaseIdToken', `Bearer ${res.data.token}`);
this.props.history.push('/home');
})
.catch(err => {
this.setState({
errors: err.response.data
});
});
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { classes } = this.props;
const { errors } = this.state;
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Log in to Twistter</b>
<br/><br/>
<form noValidate onSubmit={this.handleSubmit}>
<TextField className="authInput" id="email" name="email" label="Email" helperText={errors.email} error={errors.email ? true : false}
value={this.state.email} onChange={this.handleChange} />
<br/><br/>
<TextField className="authInput" id="password" name="password" label="Password" helperText={errors.password} error={errors.password ? true : false}
value={this.state.password} onChange={this.handleChange} />
<br/><br/>
<button className="authButtons register" type="submit">Sign in</button>
</form>
</div>
);
};
}
Login.propTypes = {
classes: PropTypes.object.isRequired
};
export default Login;

View File

@@ -0,0 +1,97 @@
import React, { Component } from 'react';
import '../App.css';
import logo from '../images/twistter-logo.png';
import axios from 'axios';
import TextField from '@material-ui/core/TextField';
import PropTypes from 'prop-types';
class Register extends Component {
constructor() {
super();
this.state = {
email: '',
handle: '',
password: '',
confirmPassword: '',
errors: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
};
handleSubmit = (event) => {
const newUserData = {
email: this.state.email,
handle: this.state.handle,
password: this.state.password,
confirmPassword: this.state.confirmPassword
};
axios.post('http://localhost:5001/twistter-e4649/us-central1/api/signup', newUserData)
.then(res => {
console.log(res.data);
localStorage.setItem('firebaseIdToken', `Bearer ${res.data.token}`);
this.props.history.push('/');
})
.catch(err => {
this.setState({
errors: err.response.data
});
});
alert("You successfully registered");
event.preventDefault();
this.setState({email: '', handle: '', password: '', confirmPassword: ''});
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { classes } = this.props;
const { errors } = this.state;
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Create your account</b>
<br/><br/>
<form noValidate onSubmit={this.handleSubmit}>
<TextField className="authInput" id="email" name="email" label="Email" helperText={errors.email} error={errors.email ? true : false}
value={this.state.email} onChange={this.handleChange}/>
<br/><br/>
<TextField className="authInput" id="username" name="handle" label="Username" helperText={errors.handle} error={errors.handle ? true : false}
value={this.state.handle} onChange={this.handleChange} />
<br/><br/>
<TextField className="authInput" id="password" name="password" label="Password" helperText={errors.password} error={errors.password ? true : false}
value={this.state.password} onChange={this.handleChange} />
<br/><br/>
<TextField className="authInput" id="confirmPassword" name="confirmPassword" label="Confirm Password" helperText={errors.confirmPassword} error={errors.confirmPassword ? true : false}
value={this.state.confirmPassword} onChange={this.handleChange} />
<br/><br/>
{
errors.general &&
(<div className={classes.customError}>
{errors.general}
</div>)
}
<button class="authButtons register" id="submit">Sign up</button>
</form>
</div>
);
}
}
Register.propTypes = {
classes: PropTypes.object.isRequired
};
export default Register;

View File

@@ -0,0 +1,224 @@
import React, { Component } from "react";
import axios from "axios";
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
// Material-UI stuff
import Button from "@material-ui/core/Button";
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";
const styles = {
form: {
textAlign: "center"
},
textField: {
marginBottom: 30
},
pageTitle: {
// marginTop: 20,
marginBottom: 40
},
button: {
positon: "relative",
marginBottom: 30
},
progress: {
position: "absolute"
}
};
export class edit extends Component {
// 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() {
axios
.get("/getProfileInfo")
.then((res) => {
this.setState({
firstName: res.data.firstName,
lastName: res.data.lastName,
email: res.data.email,
handle: res.data.handle,
bio: res.data.bio
});
})
.catch((err) => {
console.error(err);
});
}
// Constructor for the state
constructor() {
super();
this.state = {
firstName: "",
lastName: "",
email: "",
handle: "",
bio: "",
loading: false,
errors: {}
};
}
// Runs whenever the submit button is clicked.
// Updates the database entry of the signed in user with the
// data stored in the state.
handleSubmit = (event) => {
event.preventDefault();
this.setState({
loading: true
});
const newProfileData = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email: this.state.email,
handle: this.state.handle,
bio: this.state.bio
};
axios
.post("/updateProfileInfo", newProfileData)
.then((res) => {
this.setState({
loading: false
});
// this.props.history.push('/');
// TODO: Need to redirect user to their profile page
})
.catch((err) => {
this.setState({
errors: err.response.data,
loading: false
});
});
};
// Updates the state whenever one of the textboxes changes.
// The key is the name of the textbox and the value is the
// value in the text box.
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
errors: {
[event.target.name]: null
}
});
};
render() {
const { classes } = this.props;
const { errors, loading } = this.state;
return (
<Grid container className={classes.form}>
<Grid item sm />
<Grid item sm>
<Typography variant="h2" className={classes.pageTitle}>
Edit Profile
</Typography>
<form noValidate onSubmit={this.handleSubmit}>
<Grid container className={classes.form} spacing={4}>
<Grid item sm>
<TextField
id="firstName"
name="firstName"
label="First Name"
className={classes.textField}
value={this.state.firstName}
helperText={errors.firstName}
error={errors.firstName ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
</Grid>
<Grid item sm>
<TextField
id="lastName"
name="lastName"
label="Last Name"
className={classes.textField}
value={this.state.lastName}
helperText={errors.lastname}
error={errors.lastName ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
</Grid>
</Grid>
<TextField
id="email"
name="email"
label="Email*"
className={classes.textField}
value={this.state.email}
disabled
helperText="(disabled)"
// INFO: These will be uncommented if changing emails is allowed
// helperText={errors.email}
// error={errors.email ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
<TextField
id="handle"
name="handle"
label="Handle*"
className={classes.textField}
value={this.state.handle}
disabled
helperText="(disabled)"
// INFO: These will be uncommented if changing usernames is allowed
// helperText={errors.handle}
// error={errors.handle ? true : false}
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
<TextField
id="bio"
name="bio"
label="Bio"
className={classes.textField}
value={this.state.bio}
helperText={errors.bio}
error={errors.bio ? true : false}
multiline
rows="8"
variant="outlined"
onChange={this.handleChange}
fullWidth
/>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
disabled={loading}
>
Submit
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</form>
</Grid>
<Grid item sm />
</Grid>
);
}
}
edit.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(edit);

View File

@@ -0,0 +1,35 @@
/* eslint-disable */
import React, { Component } from 'react';
//import '../App.css';
import { makeStyles, styled } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardMedia from '@material-ui/core/CardMedia';
import CardContent from '@material-ui/core/CardContent';
const PostCard = styled(Card)({
background: 'linear-gradient(45deg, #1da1f2 90%)',
border: 3,
borderRadius: 3,
height:225,
width: 645,
padding: '0 30px',
});
class user extends Component {
render() {
return (
<div>
<h1>User Profile</h1>
<br/><br/>
<PostCard>Some card and content</PostCard>
<br/><br/>
</div>
)
}
}
export default user;

View File

@@ -0,0 +1,77 @@
/* eslint-disable */
import React, { Fragment } from 'react';
import NoImg from '../images/no-img.png';
import PropTypes from 'prop-types';
// MUI
import Card from '@material-ui/core/Card';
import CardMedia from '@material-ui/core/CardMedia';
import CardContent from '@material-ui/core/CardContent';
import withStyles from '@material-ui/core/styles/withStyles';
const styles = (theme) => ({
...theme,
card: {
display: 'flex',
marginBottom: 20
},
cardContent: {
width: '100%',
flexDirection: 'column',
padding: 25
},
cover: {
minWidth: 200,
objectFit: 'cover'
},
handle: {
width: 60,
height: 18,
backgroundColor: theme.palette.primary.main,
marginBottom: 7
},
date: {
height: 14,
width: 100,
backgroundColor: 'rgba(0,0,0, 0.3)',
marginBottom: 10
},
fullLine: {
height: 15,
width: '90%',
backgroundColor: 'rgba(0,0,0, 0.6)',
marginBottom: 10
},
halfLine: {
height: 15,
width: '50%',
backgroundColor: 'rgba(0,0,0, 0.6)',
marginBottom: 10
}
});
const PostSkeleton = (props) => {
const { classes } = props;
const content = Array.from({ length: 5 }).map((item, index) => (
<Card className={classes.card} key={index}>
<CardMedia className={classes.cover} image={NoImg} />
<CardContent className={classes.cardContent}>
<div className={classes.handle} />
<div className={classes.date} />
<div className={classes.fullLine} />
<div className={classes.fullLine} />
<div className={classes.halfLine} />
</CardContent>
</Card>
));
return <Fragment>{content}</Fragment>;
};
PostSkeleton.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(PostSkeleton);

View File

@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import withStyles from '@material-ui/core/styles/withStyles';
import NoImg from '../images/no-img.png';
// MUI
import Paper from '@material-ui/core/Paper';
// Icons
import LocationOn from '@material-ui/icons/LocationOn';
import LinkIcon from '@material-ui/icons/Link';
import CalendarToday from '@material-ui/icons/CalendarToday';
const styles = (theme) => ({
...theme,
handle: {
height: 20,
backgroundColor: theme.palette.primary.main,
width: 60,
margin: '0 auto 7px auto'
},
fullLine: {
height: 15,
backgroundColor: 'rgba(0,0,0,0.6)',
width: '100%',
marginBottom: 10
},
halfLine: {
height: 15,
backgroundColor: 'rgba(0,0,0,0.6)',
width: '50%',
marginBottom: 10
}
});
const ProfileSkeleton = (props) => {
const { classes } = props;
return (
<Paper className={classes.paper}>
<div className={classes.profile}>
<div className="image-wrapper">
<img src={NoImg} alt="profile" className="profile-image" />
</div>
<hr />
<div className="profile-details">
<div className={classes.handle} />
<hr />
<div className={classes.fullLine} />
<div className={classes.fullLine} />
<hr />
<LocationOn color="primary" /> <span>Location</span>
<hr />
<LinkIcon color="primary" /> https://website.com
<hr />
<CalendarToday color="primary" /> Joined date
</div>
</div>
</Paper>
);
};
ProfileSkeleton.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(ProfileSkeleton);

View File

@@ -0,0 +1,97 @@
export default {
palette: {
primary: {
light: '#1da1f2',
main: '#1da1f2',
dark: '#008394',
contrastText: '#fff'
},
secondary: {
light: '#ff6333',
main: '#ff3d00',
dark: '#b22a00',
contrastText: '#fff'
}
},
typography: {
useNextVariants: true
},
form: {
textAlign: 'center'
},
image: {
margin: '20px auto 20px auto'
},
pageTitle: {
margin: '10px auto 10px auto'
},
textField: {
margin: '10px auto 10px auto'
},
button: {
marginTop: 20,
position: 'relative'
},
customError: {
color: 'red',
fontSize: '0.8rem',
marginTop: 10
},
progress: {
position: 'absolute'
},
invisibleSeparator: {
border: 'none',
margin: 4
},
visibleSeparator: {
width: '100%',
borderBottom: '1px solid rgba(0,0,0,0.1)',
marginBottom: 20
},
paper: {
padding: 20
},
profile: {
'& .image-wrapper': {
textAlign: 'center',
position: 'relative',
'& button': {
position: 'absolute',
top: '80%',
left: '70%'
}
},
'& .profile-image': {
width: 200,
height: 200,
objectFit: 'cover',
maxWidth: '100%',
borderRadius: '50%'
},
'& .profile-details': {
textAlign: 'center',
'& span, svg': {
verticalAlign: 'middle'
},
'& a': {
color: '#00bcd4'
}
},
'& hr': {
border: 'none',
margin: '0 0 10px 0'
},
'& svg.button': {
'&:hover': {
cursor: 'pointer'
}
}
},
buttons: {
textAlign: 'center',
'& a': {
margin: '20px 10px'
}
}
};