Merge branch 'master' into edit-profile-info

This commit is contained in:
Clayton Wilson 2019-09-29 21:33:41 -04:00 committed by GitHub
commit 7f2d6a3a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 4770 additions and 1947 deletions

View File

@ -0,0 +1,27 @@
/* eslint-disable promise/always-return */
exports.putPost = (req, res) => {
if (req.body.body.trim() === '') {
return res.status(400).json({ body: 'Body must not be empty!'});
}
const newPost = {
body: req.body.body,
userHandle: req.user.handle,
userImage: req.user.imageUrl,
createdAt: new Date().toISOString(),
likeCount: 0,
commentCount: 0
};
db.collection('post').add(newPost)
.then((doc) => {
const resPost = newPost;
resPost.postId = doc.id;
res.json(resPost);
})
.catch((err) => {
res.status(500).json({ error: 'something is wrong'});
console.error(err);
});
};

View File

@ -37,4 +37,37 @@ exports.updateProfileInfo = (req, res) => {
error: 'Error updating profile data' 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,13 +1,18 @@
/* eslint-disable promise/always-return */ /* eslint-disable promise/always-return */
const functions = require('firebase-functions'); const functions = require('firebase-functions');
const app = require('express')(); const app = require('express')();
const cors = require('cors');
app.use(cors());
const FBAuth = require('./util/FBAuth'); const FBAuth = require('./util/FBAuth');
/*------------------------------------------------------------------* /*------------------------------------------------------------------*
* users.js * * handlers/users.js *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
const {getProfileInfo, updateProfileInfo} = require('./handlers/users'); const {getUserDetails, getProfileInfo, updateProfileInfo} = require('./handlers/users');
// Returns all data in the users collection
app.get('/getUser/:handle', getUserDetails);
// Returns all profile data of the currently logged in user // Returns all profile data of the currently logged in user
// TODO: Add FBAuth // TODO: Add FBAuth
@ -17,5 +22,13 @@ app.get('/getProfileInfo', getProfileInfo);
// TODO: Add FBAuth // TODO: Add FBAuth
app.post('/updateProfileInfo', updateProfileInfo); app.post('/updateProfileInfo', updateProfileInfo);
/*------------------------------------------------------------------*
* handlers/post.js *
*------------------------------------------------------------------*/
const {putPost} = require('./handlers/post');
// Adds one post to the database
app.post('/putPost', FBAuth, putPost);
exports.api = functions.https.onRequest(app); 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": {
@ -90,17 +105,32 @@
} }
}, },
"@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",
"integrity": "sha512-CPYLvkGZBKE47oQC9a0q13UMVRj3LvnSbB1nOerktE3CGRHKy44LxDumamN8Kj067hV/80mKK9FdbeUufwO/Rg==", "integrity": "sha512-O/yAbXpitOA6g627cUl0/FHYlkTy1EiEKMKOlnlMOJF2fH+nLVZREXjsrCC7N2tIvTn7yYwfpZ4zpSNvrhwiTA==",
"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": {
@ -109,9 +139,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",
@ -125,14 +155,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": {
@ -146,13 +186,23 @@
"integrity": "sha512-wPwhWCepEjWiTIqeC9U+7Hcw4XwezKPdXmyXbYSPiWNDcVekNgMPkntwSK+/2ufJO/1nMwAL2n6fL12oQG/PpQ==" "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": {
@ -161,15 +211,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": {
@ -178,9 +243,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",
@ -195,13 +260,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": {
@ -1453,35 +1528,48 @@
} }
}, },
"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"
}
} }
} }
}, },

View File

@ -13,7 +13,7 @@
"node": "8" "node": "8"
}, },
"dependencies": { "dependencies": {
"firebase": "^6.6.1", "firebase": "^6.6.2",
"firebase-admin": "^8.6.0", "firebase-admin": "^8.6.0",
"firebase-functions": "^3.1.0" "firebase-functions": "^3.1.0"
}, },

View File

@ -4,4 +4,4 @@ admin.initializeApp();
const db = admin.firestore(); const db = admin.firestore();
module.exports = {admin, db}; module.exports = { admin, db };

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

@ -0,0 +1,28 @@
const { admin, db } = require('./admin');
module.exports = (req, res, next) => {
let idToken;
if (req.headers.authorization) {
idToken = req.headers.authorization;
} else {
console.error('No token found');
return res.status(403).json({ error: 'Unauthorized'});
}
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;
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

@ -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,18 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.4.3",
"axios": "^0.19.0",
"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": {

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

@ -41,3 +41,8 @@
border: 0px; border: 0px;
padding: 10px; padding: 10px;
} }
.container {
margin: 80px auto 0 auto;
max-width: 1200px;
}

View File

@ -1,172 +1,32 @@
/* eslint-disable */
import React, { Component } from 'react'; import React, { Component } from 'react';
import logo from './twistter-logo.png';
import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route';
import './App.css'; import './App.css';
var validEmail = true; import { BrowserRouter as Router } from 'react-router-dom';
var validUsername = false; import Route from 'react-router-dom/Route';
var validPassword = false;
var passwordsMatch = false;
const emailBlur = () => {
//var email = document.getElementById("email");
/*if() { import home from './Home.js';
validEmail = true; import register from './Register.js';
} import login from './Login.js';
else { import user from './pages/user';
validEmail = false; import writeMicroblog from './Writing_Microblogs.js';
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> <Router>
<div className="app"> <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 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>
</Router> </Router>
); );
} }

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,28 @@
/* eslint-disable */
import React, { Component } from 'react';
import './App.css';
import logo from './images/twistter-logo.png';
import TextField from '@material-ui/core/TextField';
import axios from 'axios';
class Login extends Component {
render() {
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Log in to Twistter</b>
<br/><br/>
<TextField className="authInput" id="email" name="email" label="Email" />
<br/><br/>
<TextField className="authInput" id="password" name="password" label="Password" />
<br/><br/>
<button className="authButtons register" type="submit">Sign in</button>
</div>
);
};
}
export default Login;

View File

@ -0,0 +1,31 @@
import React, { Component } from 'react';
import './App.css';
import logo from './images/twistter-logo.png';
import TextField from '@material-ui/core/TextField';
//import axios from 'axios';
class Register extends Component {
render() {
return (
<div>
<img src={logo} className="app-logo" alt="logo" />
<br/><br/>
<b>Create your account</b>
<br/><br/>
<TextField className="authInput" id="email" name="email" label="Email" />
<br/><br/>
<TextField className="authInput" id="username" name="username" label="Username" />
<br/><br/>
<TextField className="authInput" id="password" name="password" label="Password" />
<br/><br/>
<TextField className="authInput" id="confirmPassword" name="confirmPassword" label="Confirm Password" />
<br/><br/>
<button class="authButtons register" id="submit">Sign up</button>
</div>
);
}
}
export default Register;

View File

@ -0,0 +1,72 @@
import React, { Component } from "react";
import { BrowserRouter as Router } from 'react-router-dom';
import Route from 'react-router-dom/Route';
class Writing_Microblogs extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
title: '',
characterCount: 10
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeforPost = this.handleChangeforPost.bind(this);
}
handleChange(event) {
this.setState( {title: event.target.value });
}
handleSubmit(event) {
alert('A title for the microblog was inputted: ' + this.state.title + '\nA microblog was posted: ' + this.state.value);
event.preventDefault();
}
handleChangeforPost(event) {
this.setState({value: event.target.value })
}
handleChangeforCharacterCount(event) {
const charCount = event.target.value.length
const charRemaining = 10 - charCount
this.setState({characterCount: charRemaining })
}
render() {
return (
<div>
<div style={{ width: "200px", height: "50px", marginTop: "180px", marginLeft: "30px" }}>
<form>
<input type="text" placeholder="Enter Microblog Title" value={this.state.title} onChange={this.handleChange} />
</form>
</div>
<div style={{ width: "200px", marginLeft: "50px"}}>
<form onSubmit={this.handleSubmit}>
<textarea value={this.state.value} required maxLength="10" placeholder= "Write Microblog here..."
onChange = { (e) => { this.handleChangeforPost(e); this.handleChangeforCharacterCount(e) } } cols={40} rows={20} />
<div style={{ fontSize: "14px", marginRight: "-100px"}} >
<p2>Characters Left: {this.state.characterCount}</p2>
</div>
<div style={{ marginRight: "-100px" }}>
<button onClick>Share Post</button>
</div>
</form>
</div>
</div>
);
}
}
export default Writing_Microblogs;

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='/user'>
User
</Button>
<Button component={ Link } to='/login'>
Login
</Button>
<Button component={ Link } to='/register'>
Register
</Button>
<Button component={ Link } to='/'>
Home
</Button>
</ToolBar>
</AppBar>
)
}
}
export default Navbar;

View File

@ -0,0 +1,52 @@
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';
const useStyles = 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,40 @@
/* eslint-disable */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import StaticProfile from '../components/profile/StaticProfile';
import Grid from '@material-ui/core/Grid';
import PostSkeleton from '../util/PostSkeleton';
import { connect } from 'react-redux';
class user extends Component {
render() {
const postMarkup = PostSkeleton;
return (
<b>User page</b>
// <Grid container spacing={16}>
// <Grid item sm={8} xs={12}>
// <b>postMarkup</b>
// {postMarkup}
// </Grid>
// {/* <Grid item sm={4} xs={12}>
// <StaticProfile profile={this.state.profile} />
// </Grid> */}
// </Grid>
)
}
}
user.propTypes = {
// getUserData: PropTypes.func.isRequired,
//data: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
data: state.data
});
export default connect(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);