diff --git a/.env b/.env
deleted file mode 100644
index c8587ae..0000000
--- a/.env
+++ /dev/null
@@ -1,18 +0,0 @@
-# Daksh Dummy Account Details:
-REACT_APP_FIREBASE_API_KEY = "AIzaSyBC5RfffIrdsCG-mHVfRF0bUGiG8Sx5hGs"
-REACT_APP_FIREBASE_AUTH_DOMAIN = "coderoom-aecd1.firebaseapp.com"
-REACT_APP_FIREBASE_DATABASE_URL = "https://coderoom-aecd1.firebaseio.com"
-REACT_APP_FIREBASE_PROJECT_ID = "coderoom-aecd1"
-REACT_APP_FIREBASE_STORAGE_BUCKET = "coderoom-aecd1.appspot.com"
-REACT_APP_FIREBASE_MESSAGING_SENDER_ID = "127836959872"
-REACT_APP_FIREBASE_APP_ID = "1:127836959872:web:d48df173099d46aba76265"
-
-# Arpansac Account Details:
-# REACT_APP_FIREBASE_API_KEY = "AIzaSyCyg3-TiRdd_8qP1q3TwY7EA_9iPOPpbE4",
-# REACT_APP_FIREBASE_AUTH_DOMAIN = "coderoom-dev.firebaseapp.com",
-# REACT_APP_FIREBASE_DATABASE_URL = "https://coderoom-dev.firebaseio.com"
-# REACT_APP_FIREBASE_PROJECT_ID = "coderoom-dev"
-# REACT_APP_FIREBASE_STORAGE_BUCKET = "coderoom-dev.appspot.com"
-# REACT_APP_FIREBASE_MESSAGING_SENDER_ID = "694801682636"
-# REACT_APP_FIREBASE_APP_ID = "1:694801682636:web:1f0b9ed0900cdd15a8f269"
-# REACT_APP_FIREBASE_MEASUREMENT_ID = "G-2S2MQSLD75"
\ No newline at end of file
diff --git a/.firebase/hosting.YnVpbGQ.cache b/.firebase/hosting.YnVpbGQ.cache
deleted file mode 100644
index 3a96652..0000000
--- a/.firebase/hosting.YnVpbGQ.cache
+++ /dev/null
@@ -1,17 +0,0 @@
-index.html,1584440977945,4a6e784316f8ddc0f0c81ad0182a50fdfffbbd7f1ebced0e66137dbcea2dae17
-asset-manifest.json,1584440977946,79ca0fa23d0a6a884eb7aca584e5e72ccbbc7444a12d4f3dfe9eaa1247bc06c8
-static/css/2.fba55a0b.chunk.css,1584440977992,924c60cbb1fca274040a2a2c55942950e947e8e61f69e8802b30d3476d8eacff
-static/css/main.d8a4147f.chunk.css,1584440977989,bac81478843f9fbddbdff9a1d72d6be7dfb808043a4417a0cc00533b0005cdd7
-service-worker.js,1584440977945,8317aa7ad4218d9b6226af223cf1584702332500a77d3d27c592782fcab4d0d2
-precache-manifest.f72ce769d1b7e696ce0db0701320908d.js,1584440977945,d1f3ae731daa15217ebd16557b220acb73555b1c7c4e72631400600924c96c05
-static/css/main.d8a4147f.chunk.css.map,1584440977992,752746cdd91442746a96efafffd79bf909b679c2c2ba7b2d8d7be0e1c152a5ac
-static/js/2.4584098a.chunk.js.LICENSE.txt,1584440977992,faf342c917769a205d99b2a900b027858d73851af8df1fa3c051475ebff59070
-static/css/2.fba55a0b.chunk.css.map,1584440977992,aef0708a2c016b136c003417e6a3741e4d99065ef70d706b0c67b676c5b1ae2f
-static/js/main.8de944e0.chunk.js,1584440977946,fb673321c463c1ebc8f105050181afb889ff9b9ad3a8779d8e65dc5f91750444
-favicon_CN.ico,1584440960136,2f5e8db0721f2d0efe10f64b8099530466cedf37a9a92d57c3f7ed2b27ff8de5
-static/js/runtime-main.e13d1eff.js,1584440977990,1d2dbb0095a527cc8dcbb8746b42e14550f4543a28480671bc5821e47c552fd9
-static/js/main.8de944e0.chunk.js.map,1584440977992,d0a2215125f080537648641cc989625df5a404c5a5478cd20aa2870234adff87
-static/media/CNLOGO.c149ff8b.svg,1584440977986,bd340e9acd31b3bdf5732250d141ccb8e2e6247670834b5605a10e4e91a5fb9f
-static/js/runtime-main.e13d1eff.js.map,1584440977990,1b8351c356a25c434cb32880147976019bcc9e1553fad7e6afb83bee61160729
-static/js/2.4584098a.chunk.js,1584440977993,8dba1b39dd2cf5fa8a42622c0ac5159ba41bbf1c132ba26bd7f03cc06e93f728
-static/js/2.4584098a.chunk.js.map,1584440977992,a615cf8a0bd5ad4ced78ca3f84c7a10b979aaeb3fcbe4a428a12be1f630a2268
diff --git a/.firebaserc b/.firebaserc
index 865636a..c9627e4 100644
--- a/.firebaserc
+++ b/.firebaserc
@@ -1,5 +1,5 @@
{
"projects": {
- "default": "coderoom-aecd1"
+ "default": "coderoom-app"
}
}
diff --git a/README.md b/README.md
index a97e951..5608f3f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,15 @@
# Coderoom | Coding Ninjas
+### (http://coderoom-app.firebaseapp.com)
A collaborative place for sharing code in realtime!
-## To run the project:
+## To run the project (locally on your machine):
- Clone the project.
- Run command `npm install` to install all the dependencies required.
-- Run command `npm run build` in the terminal to build the project.
+
+#### For development mode:
+- Run command `npm start` in the terminal to run the project in development mode.
+
+#### For production mode:
+- Run command `npm run build` in the terminal to build the project for production mode.
+- Initialise the project using Firebase command-line tools and run command `firebase init`.
- Run command `firebase serve` in the terminal to start the Web App at `http://localhost:5000/`.
\ No newline at end of file
diff --git a/database.rules.json b/database.rules.json
index f122d7b..b104e9c 100644
--- a/database.rules.json
+++ b/database.rules.json
@@ -1,5 +1,4 @@
{
- /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
".read": true,
".write": true
diff --git a/package-lock.json b/package-lock.json
index 069d56f..5e8d282 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1376,6 +1376,35 @@
"resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.35.tgz",
"integrity": "sha512-7njiGBbFW0HCnuKNEJLcQt9EjfOzG8EJiXlFJwA3XfgiFxPVHmXrcF4d5yold2wfiwCwrXpeNTGZ854oRr6Hcw=="
},
+ "@fortawesome/fontawesome-common-types": {
+ "version": "0.2.27",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.27.tgz",
+ "integrity": "sha512-97GaByGaXDGMkzcJX7VmR/jRJd8h1mfhtA7RsxDBN61GnWE/PPCZhOdwG/8OZYktiRUF0CvFOr+VgRkJrt6TWg=="
+ },
+ "@fortawesome/fontawesome-svg-core": {
+ "version": "1.2.27",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.27.tgz",
+ "integrity": "sha512-sOD3DKynocnHYpuw2sLPnTunDj7rLk91LYhi2axUYwuGe9cPCw7Bsu9EWtVdNJP+IYgTCZIbyARKXuy5K/nv+Q==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.27"
+ }
+ },
+ "@fortawesome/free-solid-svg-icons": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.12.1.tgz",
+ "integrity": "sha512-k3MwRFFUhyL4cuCJSaHDA0YNYMELDXX0h8JKtWYxO5XD3Dn+maXOMrVAAiNGooUyM2v/wz/TOaM0jxYVKeXX7g==",
+ "requires": {
+ "@fortawesome/fontawesome-common-types": "^0.2.27"
+ }
+ },
+ "@fortawesome/react-fontawesome": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.9.tgz",
+ "integrity": "sha512-49V3WNysLZU5fZ3sqSuys4nGRytsrxJktbv3vuaXkEoxv22C6T7TEG0TW6+nqVjMnkfCQd5xOnmJoZHMF78tOw==",
+ "requires": {
+ "prop-types": "^15.7.2"
+ }
+ },
"@google-cloud/paginator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz",
@@ -1697,6 +1726,11 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
},
+ "@popperjs/core": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.1.1.tgz",
+ "integrity": "sha512-sLqWxCzC5/QHLhziXSCAksBxHfOnQlhPRVgPK0egEw+ktWvG75T2k+aYWVjVh9+WKeT3tlG3ZNbZQvZLmfuOIw=="
+ },
"@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -1751,6 +1785,20 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
},
+ "@restart/context": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
+ "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q=="
+ },
+ "@restart/hooks": {
+ "version": "0.3.22",
+ "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.22.tgz",
+ "integrity": "sha512-tW0T3hP6emYNOc76/iC96rlu+f7JYLSVk/Wnn+7dj1gJUcw4CkQNLy16vx2mBLtVKsFMZ9miVEZXat8blruDHQ==",
+ "requires": {
+ "lodash": "^4.17.15",
+ "lodash-es": "^4.17.15"
+ }
+ },
"@sheerun/mutationobserver-shim": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
@@ -3326,6 +3374,11 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
+ "bootstrap": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
+ "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA=="
+ },
"bowser": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz",
@@ -10569,6 +10622,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
+ "lodash-es": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
+ "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
+ },
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz",
@@ -13307,6 +13365,25 @@
"react-is": "^16.8.1"
}
},
+ "prop-types-extra": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
+ "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
+ "requires": {
+ "react-is": "^16.3.2",
+ "warning": "^4.0.0"
+ },
+ "dependencies": {
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
"protobufjs": {
"version": "6.8.8",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
@@ -13534,6 +13611,65 @@
"whatwg-fetch": "^3.0.0"
}
},
+ "react-bootstrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0.tgz",
+ "integrity": "sha512-Ep6ZNH6wL5m9bytOS6T9mjSz0YE1bEkc+uHItvenRcA3amr5ApkpKYzAWgdglhRPZHPvm+pnqs1z5IPwv/2UZw==",
+ "requires": {
+ "@babel/runtime": "^7.4.2",
+ "@restart/context": "^2.1.4",
+ "@restart/hooks": "^0.3.21",
+ "@types/react": "^16.9.23",
+ "classnames": "^2.2.6",
+ "dom-helpers": "^5.1.2",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2",
+ "prop-types-extra": "^1.1.0",
+ "react-overlays": "^3.0.1",
+ "react-transition-group": "^4.0.0",
+ "uncontrollable": "^7.0.0",
+ "warning": "^4.0.3"
+ },
+ "dependencies": {
+ "@types/react": {
+ "version": "16.9.26",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.26.tgz",
+ "integrity": "sha512-dGuSM+B0Pq1MKXYUMlUQWeS6Jj9IhSAUf9v8Ikaimj+YhkBcQrihWBkmyEhK/1fzkJTwZQkhZp5YhmWa2CH+Rw==",
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^2.2.0"
+ }
+ },
+ "dom-helpers": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
+ "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "csstype": "^2.6.7"
+ }
+ },
+ "react-transition-group": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
+ "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ }
+ },
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
"react-codemirror": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-codemirror/-/react-codemirror-1.0.0.tgz",
@@ -13757,6 +13893,44 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
},
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-overlays": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-3.0.1.tgz",
+ "integrity": "sha512-QEt6I3Cjy06pe2FwY/tuWaXEzSVOuXfP8zsC6oWHJhMYpEJQgZV/TCwbCw5slMW6VcgwcWPc4HrBzN0yfxf5Xw==",
+ "requires": {
+ "@babel/runtime": "^7.4.5",
+ "@popperjs/core": "^2.0.0",
+ "@restart/hooks": "^0.3.12",
+ "dom-helpers": "^5.1.0",
+ "prop-types": "^15.7.2",
+ "uncontrollable": "^7.0.0",
+ "warning": "^4.0.3"
+ },
+ "dependencies": {
+ "dom-helpers": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
+ "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "csstype": "^2.6.7"
+ }
+ },
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
"react-router": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
@@ -16210,6 +16384,17 @@
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
},
+ "uncontrollable": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz",
+ "integrity": "sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "@types/react": "^16.9.11",
+ "invariant": "^2.2.4",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
diff --git a/package.json b/package.json
index e4d555c..68ac02a 100644
--- a/package.json
+++ b/package.json
@@ -3,15 +3,20 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^1.2.27",
+ "@fortawesome/free-solid-svg-icons": "^5.12.1",
+ "@fortawesome/react-fontawesome": "^0.1.9",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
+ "bootstrap": "^4.4.1",
"env-cmd": "^10.1.0",
"firebase": "^7.9.1",
"firebase-tools": "^7.13.1",
"material-ui": "^0.20.2",
"random-key": "^0.3.2",
"react": "^16.12.0",
+ "react-bootstrap": "^1.0.0",
"react-codemirror": "^1.0.0",
"react-dom": "^16.12.0",
"react-helmet": "^5.2.1",
diff --git a/public/index.html b/public/index.html
index d9236a9..7c13a95 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4,8 +4,40 @@
+
Code Room | Coding Ninjas
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Backdrop/Backdrop.css b/src/components/Backdrop/Backdrop.css
new file mode 100644
index 0000000..4045a88
--- /dev/null
+++ b/src/components/Backdrop/Backdrop.css
@@ -0,0 +1,9 @@
+.backdrop {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background: rgba(0,0,0,0.3);
+ z-index: 100;
+}
\ No newline at end of file
diff --git a/src/components/Backdrop/Backdrop.js b/src/components/Backdrop/Backdrop.js
new file mode 100644
index 0000000..8c03cbc
--- /dev/null
+++ b/src/components/Backdrop/Backdrop.js
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import './Backdrop.css';
+
+const backdrop = props => (
+
+);
+
+export default backdrop;
\ No newline at end of file
diff --git a/src/components/ClonePopup/CloneReceivePopUp.css b/src/components/ClonePopup/CloneReceivePopUp.css
new file mode 100644
index 0000000..64a2dec
--- /dev/null
+++ b/src/components/ClonePopup/CloneReceivePopUp.css
@@ -0,0 +1,14 @@
+.popup-heading {
+ font-size: 1.2rem;
+ text-align: center;
+}
+
+.popup-heading a {
+ margin-left: 8px;
+ font-size: 1.7rem;
+}
+
+.popup-heading a:hover {
+ color: red;
+ text-decoration: none;
+}
\ No newline at end of file
diff --git a/src/components/ClonePopup/CloneReceivePopUp.js b/src/components/ClonePopup/CloneReceivePopUp.js
new file mode 100644
index 0000000..1ec6c4c
--- /dev/null
+++ b/src/components/ClonePopup/CloneReceivePopUp.js
@@ -0,0 +1,50 @@
+import React, { Component } from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import 'bootstrap/dist/css/bootstrap.min.css';
+import './CloneReceivePopup.css';
+import { creatorInfo } from '../../containers/pages/Coding';
+import { newCloneSessionID } from '../SideDrawer/SideDrawer';
+
+export default class CloneReceivePopup extends Component {
+
+ render(){
+
+ var session_id = newCloneSessionID.session_id;
+
+ return (
+
+
+
+
+ Copy received from {creatorInfo.user_name}!
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/ClonePopup/CloneSendPopup.css b/src/components/ClonePopup/CloneSendPopup.css
new file mode 100644
index 0000000..b20b531
--- /dev/null
+++ b/src/components/ClonePopup/CloneSendPopup.css
@@ -0,0 +1,4 @@
+.popup-heading {
+ font-size: 1.2rem;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/src/components/ClonePopup/CloneSendPopup.js b/src/components/ClonePopup/CloneSendPopup.js
new file mode 100644
index 0000000..603c9ee
--- /dev/null
+++ b/src/components/ClonePopup/CloneSendPopup.js
@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import 'bootstrap/dist/css/bootstrap.min.css';
+import './CloneSendPopup.css';
+
+export default class CloneSendPopup extends Component {
+
+ render(){
+
+ return (
+
+
+
+
+ Clone sent!
+
+
+
+
+
+ Created {this.props.num} clone(s).
+
+
+
+
+
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/ClonePopup/CloneSendPopupSingle.js b/src/components/ClonePopup/CloneSendPopupSingle.js
new file mode 100644
index 0000000..4ad6164
--- /dev/null
+++ b/src/components/ClonePopup/CloneSendPopupSingle.js
@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { Modal, Button } from 'react-bootstrap';
+import 'bootstrap/dist/css/bootstrap.min.css';
+import './CloneSendPopup.css';
+
+export default class CloneSendPopupSingle extends Component {
+
+ render(){
+
+ return (
+
+
+
+
+ Clone sent!
+
+
+
+
+
+ Created 1 clone.
+
+
+
+
+
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/components/Header.js b/src/components/Header/Header.js
similarity index 64%
rename from src/components/Header.js
rename to src/components/Header/Header.js
index 7d74cbd..6673a3b 100644
--- a/src/components/Header.js
+++ b/src/components/Header/Header.js
@@ -1,17 +1,20 @@
import React from "react";
import { Link } from "react-router-dom";
+import logo from '../../images/CNLOGO.svg';
type Props = {
style: React.CSSProperties,
- extras: React.ReactHTML
+ title: React.ReactHTML,
+ extras: React.ReactHTML,
};
const Header = (props: Props) => {
return (
- Code Room | Coding Ninjas
+ Code Room |
+ {props.title}
{props.extras}
);
diff --git a/src/components/SideDrawer/SideDrawer.css b/src/components/SideDrawer/SideDrawer.css
new file mode 100644
index 0000000..800d0b9
--- /dev/null
+++ b/src/components/SideDrawer/SideDrawer.css
@@ -0,0 +1,201 @@
+.side-drawer {
+ height: 100%;
+ background: white;
+ box-shadow: 1px 0px 7px rgba(0, 0, 0, 0.5);
+ position: fixed;
+ top: 0;
+ /* left: 0; */
+ right: 0;
+ width: 70%;
+ max-width: 300px;
+ z-index: 200;
+ /* transform: translateX(-100%); */
+ transform: translateX(100%);
+ transition: transform 0.3s ease-out;
+ overflow: auto;
+}
+
+.side-drawer.open {
+ transform: translateX(0);
+}
+
+.side-drawer .heading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: 8px;
+ padding: 6px 0px;
+ color: #000000;
+ font-size: 1.2rem;
+ text-decoration: none;
+ vertical-align: center;
+}
+
+.side-drawer .divider {
+ margin: 8px auto;
+ height: 1px;
+ width: 80%;
+ background-color: #757575;
+}
+
+.side-drawer .divider-thick {
+ margin: 18px auto;
+ height: 2px;
+ width: 90%;
+ background-color: #423939cc;
+}
+
+.side-drawer .creator-info {
+ display: flex;
+ align-items: center;
+ margin: 3px 0;
+ padding: 0 40px;
+}
+
+.side-drawer .creator-info img {
+ height: 2.3rem;
+ width: 2.3rem;
+ border-radius: 50%;
+ border: 1px solid #222;
+}
+
+.side-drawer .creator-info .creator-name {
+ color: #000000;
+ font-size: 1rem;
+ margin-left: 10px;
+}
+
+.side-drawer ul {
+ margin: 0;
+ padding: 0 40px;
+ height: 75%;
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+}
+
+.side-drawer ul li {
+ margin: 3px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.side-drawer ul li img {
+ height: 2.3rem;
+ width: 2.3rem;
+ border-radius: 50%;
+ border: 1px solid #222;
+ margin: auto;
+}
+
+.side-drawer ul .user-details-list {
+ color: #000000;
+ font-size: 1rem;
+ display: flex;
+}
+
+.side-drawer ul li span .user-name {
+ color: #000000;
+ font-size: 1rem;
+ margin: auto;
+ margin-left: 10px;
+}
+
+.side-drawer ul li .single-clone-btn {
+ margin: 0;
+ font-size: 0.85rem;
+}
+
+.side-drawer ul li span:hover {
+ color: #800000;
+ cursor: default;
+}
+
+.side-drawer ul li a {
+ text-decoration: none;
+ color: #000000;
+ font-size: 1rem;
+ margin: auto;
+ margin-left: 10px;
+}
+
+.side-drawer ul li a:hover {
+ color: #800000;
+ cursor: default;
+}
+
+.side-drawer .btn-sendClone {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: -14px;
+ height: 30px;
+ background-color: #323030;
+ color: #e4e4e4;
+ font-size: 14px;
+ border: 1px solid black;
+ border-radius: 4px;
+ padding: 0 8px;
+ line-height: 30px;
+ -webkit-transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+ -o-transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+ transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+.btn-sendClone:hover {
+ background: #5ce198;
+ color: black;
+}
+
+.btn-sendClone:focus {
+ outline: none;
+}
+
+.green-dot {
+ background-color: #41d641;
+ height: 10px;
+ width: 10px;
+ display: flex;
+ margin: auto;
+ margin-right: 10px;
+ margin-left: -20px;
+ border-radius: 50%;
+}
+
+.red-dot {
+ background-color: #F44336;
+ height: 10px;
+ width: 10px;
+ display: flex;
+ margin: auto;
+ margin-right: 10px;
+ margin-left: -20px;
+ border-radius: 50%;
+}
+
+.green-dot-creator {
+ background-color: #41d641;
+ height: 10px;
+ width: 10px;
+ display: flex;
+ margin-right: 10px;
+ margin-left: -20px;
+ border-radius: 50%;
+}
+
+.red-dot-creator {
+ background-color: #F44336;
+ height: 10px;
+ width: 10px;
+ display: flex;
+ margin-right: 10px;
+ margin-left: -20px;
+ border-radius: 50%;
+}
+
+/* @media (min-width: 769px) {
+ .side-drawer {
+ display: none;
+ }
+} */
\ No newline at end of file
diff --git a/src/components/SideDrawer/SideDrawer.js b/src/components/SideDrawer/SideDrawer.js
new file mode 100644
index 0000000..e4e3eff
--- /dev/null
+++ b/src/components/SideDrawer/SideDrawer.js
@@ -0,0 +1,799 @@
+import React from 'react';
+import random from "random-key";
+import './SideDrawer.css';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faFileExport } from '@fortawesome/free-solid-svg-icons';
+import { database } from "firebase/app";
+import { firebaseAuth } from "../../config/firebase-config";
+import CloneSendPopup from '../ClonePopup/CloneSendPopup';
+import CloneSendPopupSingle from '../ClonePopup/CloneSendPopupSingle';
+import CloneReceivePopup from '../ClonePopup/CloneReceivePopup';
+
+const clonesCreated = {};
+const creatorInfo = {};
+const usersList = [];
+const newCloneSessionID = {};
+
+export default class sideDrawer extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ // setting initial state
+ this.state = {
+ cloneTrigger: random.generate(3), // for triggering clone feature
+ isCreator: false,
+ clonePopupShow: false,
+ clonePopupShowSingle: false,
+ };
+
+ this.sendSingleCloneHandler = this.sendSingleCloneHandler.bind(this);
+
+ }
+
+ // setting initial state
+ state = {
+ clonePopupShow: false,
+ clonePopupShowSingle: false,
+ };
+
+ componentDidMount = () => {
+
+ const session_id = this.props.session_id;
+ var userConnectedRef = database().ref(`code-sessions/${session_id}/users-connected`);
+
+ // to fetch currently signed-in user
+ firebaseAuth().onAuthStateChanged(async (user) => {
+ try {
+ if (user) {
+
+ var creator_uid;
+
+ // fetch session creator's uid from database
+ // const creator_uid = await database()
+ await database()
+ .ref(`code-sessions/${session_id}/creator`)
+ .once("value")
+ .then(snapshot => {
+ var creatorData = snapshot.val();
+ creator_uid = creatorData.user_id;
+ creatorInfo.user_name = creatorData.user_name;
+ creatorInfo.user_photo = creatorData.user_photo;
+ // console.log(creatorData);
+ })
+ .catch(e => {
+ // no session found corresponding to "sessionid" passed in the params
+ });
+
+ if (creator_uid === undefined ){
+ // console.log("No Session Found!");
+ return;
+ }
+
+ if (user.uid === creator_uid){
+ // console.log("Current user is the session creator");
+ this.setState({ isCreator: true });
+ }
+
+ // for updating creator's presence status
+ database()
+ .ref(`code-sessions/${session_id}/creator`)
+ .on('value', snapshot => {
+ let creatorData = snapshot.val();
+ if(user.uid === creatorData.user_id){
+ let creatorRef = database().ref(`code-sessions/${session_id}/creator/user_status`);
+ // monitor connection state on browser tab
+ database().ref(".info/connected")
+ .on("value", snapshot => {
+ let userStatus = snapshot.val();
+ if(userStatus) {
+ // set session creator's online status
+ creatorRef.onDisconnect().set('offline');
+ creatorRef.set('online');
+ } else {
+ // session creator has lost network
+ }
+ });
+ }
+ });
+
+ // for updating creator's presence status
+ database()
+ .ref(`code-sessions/${session_id}/creator/user_status`)
+ .on('value', snapshot => {
+ let creatorStatus = snapshot.val();
+ if(creatorStatus === "online"){
+ // session creator is online
+ creatorInfo.user_status = "online";
+ } else if (creatorStatus === "offline"){
+ // session creator is offline
+ creatorInfo.user_status = "offline";
+ }
+ // in order to re-render the sidebar component everytime there's a change
+ this.setState(this.state);
+ });
+
+ // setting states of 'cloneTrigger' & 'isFirstLoad' in database
+ database()
+ .ref(`code-sessions/${session_id}/cloneHelper`)
+ .update({
+ cloneTrigger: this.state.cloneTrigger,
+ isFirstLoad: true,
+ });
+
+ // setting states of 'cloneTrigger' & 'isFirstLoad' in database
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper`)
+ .update({
+ cloneTrigger: this.state.cloneTrigger,
+ isFirstLoad: true,
+ });
+
+ // for updating user's presence status
+ database()
+ .ref(`code-sessions/${session_id}/users-connected`)
+ .on('value', snapshot => {
+ snapshot.forEach(function(childSnapshot){
+ let userData = childSnapshot.val();
+ if(user.uid === userData.user_id){
+ let userKey = childSnapshot.key;
+ let userRef = database().ref(`code-sessions/${session_id}/users-connected/${userKey}/user_status`);
+ // monitor connection state on browser tab
+ database().ref(".info/connected")
+ .on("value", snapshot => {
+ let userStatus = snapshot.val();
+ if(userStatus) {
+ // set user's online status
+ userRef.onDisconnect().set('offline');
+ userRef.set('online');
+ } else {
+ // user has lost network
+ }
+ });
+ return true;
+ }
+ });
+ });
+
+ // displaying users-connected from database
+ database()
+ .ref(`code-sessions/${session_id}/users-connected`)
+ .on('value', snapshot => {
+ clonesCreated.numOfClonesCreated = snapshot.numChildren();
+ console.log(`\nConnected users: ${snapshot.numChildren()}`);
+ usersList.splice(0, usersList.length);
+ var i = 0;
+
+ let self = this;
+ let isCreator = this.state.isCreator;
+
+ snapshot.forEach(function(childSnapshot){
+
+ var userData = childSnapshot.val();
+ console.log(userData.user_name + " - " + userData.user_email);
+ // console.log(userData.cloneSessionID);
+
+ let cloneLink;
+ if(userData.cloneSessionID === undefined){
+ cloneLink = `/404`;
+ } else {
+ cloneLink = `/home/${userData.cloneSessionID}`;
+ }
+
+ let displayUserNameCloneLink;
+ if(isCreator){
+ displayUserNameCloneLink =
+
+ {userData.user_name}
+
+ } else {
+ displayUserNameCloneLink =
+ {userData.user_name}
+ }
+
+ let sendSingleCloneButton;
+ if(isCreator) {
+ sendSingleCloneButton =
+
+
+
+ }
+
+ let userStatus = userData.user_status;
+ let userPresenceStatus;
+ if(userStatus === "online"){
+ // user is online
+ // console.log(userData.user_name + " is ONLINE.");
+ userPresenceStatus =
+
+ } else if (userStatus === "offline"){
+ // user is offline
+ // console.log(userData.user_name + " is OFFLINE.");
+ userPresenceStatus =
+
+ }
+
+ usersList.push(
+
+
+ { userPresenceStatus }
+
+ { displayUserNameCloneLink }
+
+ { sendSingleCloneButton }
+
+ );
+ i++;
+
+ });
+ console.log("\n");
+
+ // in order to re-render the sidebar component everytime there's a change
+ this.setState(this.state);
+
+ });
+
+ // 'received-clone' pop-up handler
+ if(!this.state.isCreator){
+ // fetching and then setting state of 'clonePopupShow' from database
+ database()
+ .ref(`code-sessions/${session_id}/cloneHelper/clonePopupShow`)
+ .on('value', snapshot => {
+ // console.log("'clonePopupShow' state changed!");
+ let clonePopupShowState = snapshot.val();
+ if(clonePopupShowState){
+ this.setState({
+ clonePopupShow: true,
+ });
+ }
+
+ // setting state of 'clonePopupShow' to false in database
+ // after delay of 500 milliseconds
+ function clonePopupShowStateDelay(){
+ database()
+ .ref(`code-sessions/${session_id}/cloneHelper/clonePopupShow`)
+ .set(false);
+ }
+ setTimeout(clonePopupShowStateDelay, 500);
+
+ });
+
+ // fetching and then setting state of 'clonePopupShow' from database
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper/clonePopupShow`)
+ .on('value', snapshot => {
+ // console.log("'clonePopupShow' state changed!");
+
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper`)
+ .once("value")
+ .then(snapshot => {
+ let isFirstLoad = snapshot.val().isFirstLoad;
+
+ // does not execute on first load of page
+ if(isFirstLoad === false){
+ let user_uid = snapshot.val().user_uid;
+
+ if(user.uid === user_uid){
+
+ let clonePopupShowState = snapshot.val();
+ if(clonePopupShowState){
+ this.setState({
+ clonePopupShow: true,
+ });
+ }
+
+ // // setting state of 'clonePopupShow' to false in database
+ // // after delay of 500 milliseconds
+ // function clonePopupShowStateDelay(){
+ // database()
+ // .ref(`code-sessions/${session_id}/singleCloneHelper/clonePopupShow`)
+ // .set(false);
+ // }
+ // setTimeout(clonePopupShowStateDelay, 500);
+
+ }
+
+ }
+
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ });
+
+ }
+
+ var date;
+
+ // 'send-single-clone' button functionality
+ // executes when 'send-single-clone' button is clicked
+ // i.e. when 'cloneTrigger' key is changed in database
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper/cloneTrigger`)
+ .on('value', snapshot => {
+
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper`)
+ .once("value")
+ .then(snapshot => {
+ let isFirstLoad = snapshot.val().isFirstLoad;
+
+ // does not execute on first load of page
+ if(isFirstLoad === false){
+
+ // console.log("sendSingleCloneButton clicked!");
+
+ let user_uid = snapshot.val().user_uid;
+
+ date = Date();
+
+ // fetching existing code and session-title in this session ID to be cloned
+ var existingContent;
+ var existingSessionTitle;
+ database()
+ .ref(`code-sessions/${session_id}`)
+ .once("value")
+ .then(snapshot => {
+ existingContent = snapshot.val().content;
+ existingSessionTitle = snapshot.val().title;
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ // only creator can create new session with cloned code
+ if(this.state.isCreator){
+
+ database()
+ .ref(`code-sessions/${session_id}/users-connected`)
+ .once("value")
+ .then(snapshot => {
+ // console.log("New clone being created.");
+
+ snapshot.forEach(function(childSnapshot){
+
+ // console.log(existingContent);
+
+ if(childSnapshot.val().user_id === user_uid){
+ let newSessionKey = random.generate(5);
+ let userData = childSnapshot.val();
+
+ // creating new session with cloned code
+ database()
+ .ref("code-sessions/" + newSessionKey)
+ .set({
+ cloneSentBy: creator_uid,
+ clonedFromSession: session_id,
+ content: existingContent,
+ createdon: date,
+ readOnly: false,
+ title: existingSessionTitle,
+ });
+
+ // adding details of the user as creator to the database
+ database()
+ .ref("code-sessions/" + newSessionKey + "/creator")
+ .set({
+ user_id: userData.user_id,
+ user_name: userData.user_name,
+ user_email: userData.user_email,
+ user_photo: userData.user_photo
+ });
+
+ return true;
+ }
+
+ });
+
+ console.log(`Clone sent!`);
+
+ })
+ .catch(e => {
+ console.log(e);
+ });
+ }
+
+ // finding new session id with cloned content
+ // for currently signed-in user
+ // and storing that session id in 'newCloneSessionID' object
+ if(!this.state.isCreator){
+
+ // console.log(date);
+ // comparing date of session created exluding the 'seconds' time
+ var dateCompressed = date.substring(0, 21);
+
+ database()
+ .ref(`code-sessions/`)
+ .once("value")
+ .then(snapshot => {
+ snapshot.forEach(function(childSnapshot){
+
+ console.clear();
+ console.log("Searching...");
+
+ let cloneSentBy = childSnapshot.child("cloneSentBy").val();
+ let user_id = childSnapshot.child("creator").child("user_id").val();
+ let content = childSnapshot.child("content").val();
+ let sessionTitle = childSnapshot.child("title").val();
+ let createdOn = childSnapshot.child("createdon").val();
+ let createdOnCompressed = createdOn.substring(0, 21);
+
+ // Comparing: content (code), user id (uid),
+ // cloneSentBy (creator id), date (createOn) & session-title
+ if(cloneSentBy === creator_uid
+ && user_id === user.uid
+ && content === existingContent
+ && sessionTitle === existingSessionTitle
+ && createdOnCompressed === dateCompressed) {
+
+ let cloneSessionID = childSnapshot.key;
+ newCloneSessionID.session_id = cloneSessionID;
+
+ console.log(`Clone found at session ID: ${cloneSessionID}`);
+
+ userConnectedRef
+ .once("value")
+ .then(snapshot => {
+ snapshot.forEach(function(childSnapshot){
+ let user_id = childSnapshot.val().user_id;
+ if(user_id === user.uid) {
+ console.log(`User details stored under "${childSnapshot.key}" in database`);
+ database()
+ .ref(`code-sessions/${session_id}/users-connected/${childSnapshot.key}`)
+ .update({
+ cloneSessionID: cloneSessionID
+ });
+ return true;
+ }
+ });
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ return true;
+ }
+
+ });
+
+ });
+ }
+
+ }
+
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ });
+
+ // 'send-clone' button functionality
+ // executes when 'send-clone' button is clicked
+ // i.e. when 'cloneTrigger' key is changed in database
+ database()
+ .ref(`code-sessions/${session_id}/cloneHelper/cloneTrigger`)
+ .on('value', snapshot => {
+
+ database()
+ .ref(`code-sessions/${session_id}/cloneHelper`)
+ .once("value")
+ .then(snapshot => {
+ let isFirstLoad = snapshot.val().isFirstLoad;
+
+ // does not execute on first load of page
+ if(isFirstLoad === false){
+
+ // console.log("sendCloneButton clicked!");
+
+ date = Date();
+
+ // fetching existing code and session-title in this session ID to be cloned
+ var existingContent;
+ var existingSessionTitle;
+ database()
+ .ref(`code-sessions/${session_id}`)
+ .once("value")
+ .then(snapshot => {
+ existingContent = snapshot.val().content;
+ existingSessionTitle = snapshot.val().title;
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ // only creator can create new sessions with cloned code
+ if(this.state.isCreator){
+
+ database()
+ .ref(`code-sessions/${session_id}/users-connected`)
+ .once("value")
+ .then(snapshot => {
+ // console.log("New clone being created.");
+
+ snapshot.forEach(function(childSnapshot){
+
+ let newSessionKey = random.generate(5);
+ let userData = childSnapshot.val();
+
+ // creating new session with cloned code
+ database()
+ .ref("code-sessions/" + newSessionKey)
+ .set({
+ cloneSentBy: creator_uid,
+ clonedFromSession: session_id,
+ content: existingContent,
+ createdon: date,
+ readOnly: false,
+ title: existingSessionTitle,
+ });
+
+ // adding details of the user as creator to the database
+ database()
+ .ref("code-sessions/" + newSessionKey + "/creator")
+ .set({
+ user_id: userData.user_id,
+ user_name: userData.user_name,
+ user_email: userData.user_email,
+ user_photo: userData.user_photo
+ });
+
+ });
+
+ // setting 'numOfClonesCreated' to be passed in props of 'CloneSendPopup'
+ clonesCreated.numOfClonesCreated = snapshot.numChildren();
+ console.log(`Created ${snapshot.numChildren()} clone(s)`);
+
+ })
+ .catch(e => {
+ console.log(e);
+ });
+ }
+
+ // finding new session id with cloned content
+ // for currently signed-in user
+ // and storing that session id in 'newCloneSessionID' object
+ if(!this.state.isCreator){
+
+ // console.log(date);
+ // comparing date of session created exluding the 'seconds' time
+ var dateCompressed = date.substring(0, 21);
+
+ database()
+ .ref(`code-sessions/`)
+ .once("value")
+ .then(snapshot => {
+
+ snapshot.forEach(function(childSnapshot){
+
+ console.clear();
+ console.log("Searching...");
+
+ let cloneSentBy = childSnapshot.child("cloneSentBy").val();
+ let user_id = childSnapshot.child("creator").child("user_id").val();
+ let content = childSnapshot.child("content").val();
+ let sessionTitle = childSnapshot.child("title").val();
+ let createdOn = childSnapshot.child("createdon").val();
+ let createdOnCompressed = createdOn.substring(0, 21);
+
+ // Comparing: content (code), user id (uid),
+ // cloneSentBy (creator id), date (createOn) & session-title
+ if(cloneSentBy === creator_uid
+ && user_id === user.uid
+ && content === existingContent
+ && sessionTitle === existingSessionTitle
+ && createdOnCompressed === dateCompressed) {
+
+ let cloneSessionID = childSnapshot.key;
+ newCloneSessionID.session_id = cloneSessionID;
+ console.log(`Clone found at session ID: ${cloneSessionID} \n`);
+
+ userConnectedRef
+ .once("value")
+ .then(snapshot => {
+ snapshot.forEach(function(childSnapshot){
+ let user_id = childSnapshot.val().user_id;
+ if(user_id === user.uid) {
+ console.log(`User details stored under "${childSnapshot.key}" in database`);
+ database()
+ .ref(`code-sessions/${session_id}/users-connected/${childSnapshot.key}`)
+ .update({
+ cloneSessionID: cloneSessionID
+ });
+ return true;
+ }
+ });
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ return true;
+ }
+ });
+
+ });
+ }
+
+ }
+
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ });
+
+ } else {
+ console.log("Cannot fetch currently signed-in user!");
+ }
+ } catch(error) {
+ console.log("Error in authentication:", error);
+ }
+ });
+
+ this.codeRef = database().ref(`code-sessions/${session_id}`);
+
+ };
+
+ // single user 'send-clone' button handler
+ sendSingleCloneHandler = (e) => {
+
+ let user_uid = e.currentTarget.value;
+ // console.log(user_uid);
+
+ let newCloneTrigger = random.generate(3);
+ this.setState({
+ clonePopupShowSingle: true,
+ cloneTrigger: newCloneTrigger,
+ });
+
+ // adding corresponding user uid into database
+ this.codeRef.child("singleCloneHelper").child("user_uid").set(user_uid);
+ // setting 'isFirstLoad' to false in database
+ this.codeRef.child("singleCloneHelper").child("isFirstLoad").set(false);
+ // setting 'clonePopupShow' state to true in database
+ this.codeRef.child("singleCloneHelper").child("clonePopupShow").set(true);
+ // updating 'cloneTrigger' value and 'clonePopupShow' state in database
+ // indicating 'send-clone' button is clicked
+ this.codeRef.child("singleCloneHelper").child("cloneTrigger").set(newCloneTrigger);
+
+ const session_id = this.props.session_id;
+ // setting state of 'clonePopupShow' to false in database
+ // after delay of 500 milliseconds
+ function clonePopupShowStateDelay(){
+ database()
+ .ref(`code-sessions/${session_id}/singleCloneHelper/clonePopupShow`)
+ .set(false);
+ }
+ setTimeout(clonePopupShowStateDelay, 800);
+
+ };
+
+ // 'send-clone' button handler
+ sendCloneHandler = () => {
+ let newCloneTrigger = random.generate(3);
+ this.setState({
+ clonePopupShow: true,
+ cloneTrigger: newCloneTrigger,
+ });
+ // setting 'isFirstLoad' to false in database
+ this.codeRef.child("cloneHelper").child("isFirstLoad").set(false);
+ // setting 'clonePopupShow' state to true in database
+ this.codeRef.child("cloneHelper").child("clonePopupShow").set(true);
+ // updating 'cloneTrigger' value and 'clonePopupShow' state in database
+ // indicating 'send-clone' button is clicked
+ this.codeRef.child("cloneHelper").child("cloneTrigger").set(newCloneTrigger);
+ };
+
+ render() {
+
+ // 'send-clone' button is only displayed when current user is the creator of session
+ let sendCloneButton;
+ if (this.state.isCreator) {
+ sendCloneButton =
+
+
+
+ }
+
+ // to close the send/receive clone popup
+ let clonePopupClose = () => {
+ this.setState({clonePopupShow: false});
+ this.setState({clonePopupShowSingle: false});
+ }
+
+ let clonePopUp;
+ if (!this.state.isCreator) {
+ // render 'clone-received' pop-up component
+ clonePopUp =
+
+ } else {
+ // render 'clone-sent' pop-up component
+ clonePopUp =
+
+ }
+
+ let singleClonePopUp;
+ if (this.state.isCreator) {
+ // render 'clone-sent' pop-up (for single user) component
+ singleClonePopUp =
+
+ }
+
+ let creatorStatus = creatorInfo.user_status;
+ let creatorPresenceStatus;
+ if(creatorStatus === "online"){
+ // user is online
+ creatorPresenceStatus =
+
+ } else if (creatorStatus === "offline"){
+ // user is offline
+ creatorPresenceStatus =
+
+ }
+
+ let drawerClasses = 'side-drawer';
+ if (this.props.show) {
+ drawerClasses = 'side-drawer open';
+ }
+
+ return (
+
+ );
+ }
+
+}
+
+// exporting new session ID with cloned code to each user-connected
+export {newCloneSessionID};
\ No newline at end of file
diff --git a/src/config/firebase-config.js b/src/config/firebase-config.js
index 31737d5..07c315d 100644
--- a/src/config/firebase-config.js
+++ b/src/config/firebase-config.js
@@ -1,4 +1,7 @@
-import firebase from 'firebase';
+import firebase from 'firebase/app';
+import 'firebase/database';
+import 'firebase/auth';
+window.firebase = firebase;
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
diff --git a/src/containers/App.css b/src/containers/App.css
index 82cfccc..fc1833b 100644
--- a/src/containers/App.css
+++ b/src/containers/App.css
@@ -1,5 +1,5 @@
.App {
- font-family: "Nunito", sans-serif;
+ font-family: "Poppins", sans-serif;
background-color: #000000;
color: #fff;
}
@@ -9,6 +9,11 @@
width: auto;
}
+.logo-header {
+ height: 40px;
+ padding-bottom: 3px;
+}
+
.App-header {
background-color: #000000;
height: 80px;
@@ -33,6 +38,45 @@
.App-title:active, .App-title:hover {
color: #fff;
+ text-decoration: none;
+}
+
+.session-title-input {
+ height: 30px;
+ width: 15rem;
+ margin-left: 5px;
+ padding: 5px;
+ background-color: #1d1f27;
+ color: #fff;
+ font-size: 19px;
+ border: 1px solid #1d1f27;
+ border-radius: 6px;
+}
+
+.session-title-input:focus {
+ outline: none;
+}
+
+.session-title-input:hover {
+ background-color: #222;
+ color: #fff;
+ border: 1px solid #ffffff6e;
+ border-radius: 6px;
+}
+
+.session-title {
+ margin-left: 50px;
+ color: #ffffffd6;
+ font-size: 17px;
+ text-decoration: none;
+}
+
+.session-title .session-title-name {
+ margin-left: 50px;
+ color: #fff;
+ font-size: 18px;
+ text-decoration: none;
+ border-bottom: 1px solid #fff;
}
.homepage {
@@ -43,6 +87,15 @@
height: calc(100vh - 80px);
}
+.firepad {
+ height: calc(100vh - 80px);
+}
+
+.powered-by-firepad {
+ display: none;
+ z-index: 0;
+}
+
.codingpage {
height: calc(100vh - 80px);
}
@@ -78,7 +131,7 @@ p {
color: #5ce198;
}
-.btn {
+.btn-home {
display: inline-block;
margin-bottom: 0;
font-weight: 500;
@@ -99,11 +152,15 @@ p {
transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
}
-.btn:hover {
+.btn-home:hover {
background: #5ce198;
color: black;
}
+.btn-home:focus {
+ outline: none;
+}
+
.btn-coding {
display: inline-block;
margin-bottom: 0;
@@ -130,6 +187,36 @@ p {
color: black;
}
+.btn-coding:focus {
+ outline: none;
+}
+
+.btn-user-editing {
+ display: inline-block;
+ margin-bottom: 0;
+ font-weight: 500;
+ text-align: center;
+ cursor: pointer;
+ border: 1px solid transparent;
+ line-height: 36px;
+ padding: 0 10px;
+ font-size: 14px;
+ border-radius: 58px;
+ text-decoration: none;
+ height: 36px;
+ background: transparent;
+ color: #e4e4e4;
+ border-color: #5ce198;
+ cursor: default;
+ -webkit-transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+ -o-transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+ transition: all 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+.btn-user-editing:focus {
+ outline: none;
+}
+
/**** Google Login Page ****/
.header-loginPage {
@@ -144,9 +231,13 @@ p {
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
color: #222;
margin-top: 5%;
+ margin-bottom: 1.3rem;
+ line-height: 1.5;
+ font-size: 2em;
+ font-weight: bold;
}
-/**** Spinning Logo ****/
+/**** Login Page Spinning Icon ****/
.lds-ring {
display: inline-block;
@@ -186,21 +277,66 @@ p {
}
}
-/**** Quick Access Classes ****/
+/**** Coding Page Spinning Icon ****/
-.margin-l-15 {
- margin-left: 15px;
+.lds-ring-coding {
+ display: inline-block;
+ width: 90px;
+ height: 90px;
+ position: absolute;
+ top: 40vh;
+ left: 46.5%;
+ z-index: 50;
+ text-align: center;
+}
+.lds-ring-coding div {
+ box-sizing: border-box;
+ display: block;
+ position: absolute;
+ width: 80px;
+ height: 80px;
+ margin: 8px;
+ border: 8px solid #fff;
+ border-radius: 50%;
+ animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+ border-color: #fff transparent transparent transparent;
+}
+.lds-ring-coding div:nth-child(1) {
+ animation-delay: -0.45s;
+}
+.lds-ring-coding div:nth-child(2) {
+ animation-delay: -0.3s;
+}
+.lds-ring-coding div:nth-child(3) {
+ animation-delay: -0.15s;
+}
+@keyframes lds-ring-coding {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
}
-.margin-l-10 {
- margin-left: 10px;
+/**** Extra ****/
+
+.mode-dropdown {
+ height: 30px;
+ font-size: 13px;
}
-.text-center {
- text-align: center;
+.toggle-btn {
+ /* vertical-align: bottom; */
+ vertical-align: middle;
+ font-size: 1.4rem;
}
-/**** Extra ****/
+.clone-btn {
+ vertical-align: bottom;
+ border-radius: 11px;
+ font-size: 1.2rem;
+}
.spotlight {
position: absolute;
@@ -211,4 +347,163 @@ p {
transparent 160px,
rgba(0, 0, 0, 0.85) 200px
);
+}
+
+/*** Dashboard ***/
+
+.dashboard {
+ font-family: "Roboto", sans-serif;
+ background-color: #fff;
+ color: #222;
+ height: calc(100vh - 80px);
+ padding: 0px 64px;
+}
+
+.dashboard .sessions-title {
+ font-family: 'Montserrat', sans-serif;
+ color: #222;
+ font-size: 34px;
+ font-weight: 400;
+ margin: 0;
+ margin-bottom: 20px;
+ padding-top: 15px;
+}
+
+.dashboard .clones-title {
+ font-family: 'Montserrat', sans-serif;
+ color: #222;
+ font-size: 34px;
+ font-weight: 400;
+ margin: 0;
+ margin-bottom: 20px;
+ margin-top: 40px;
+}
+
+.clickable {
+ cursor: pointer;
+}
+
+.aligncenter {
+ text-align: center;
+}
+
+.loading-text {
+ display: flex;
+ justify-content: center;
+ font-size: 18px;
+ font-family: monospace;
+ margin-top: 15px;
+}
+
+.view-clones-help-text {
+ display: flex;
+ justify-content: center;
+ font-size: 15px;
+ font-family: monospace;
+ margin-top: 15px;
+}
+
+table {
+ /* background-color: white; */
+ width: 80%;
+ text-align: center;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+table td, table th {
+ padding:7px;
+ text-align: center;
+}
+
+table th {
+ border-bottom: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+}
+
+.table-collection {
+ margin: auto;
+}
+
+.table-collection th {
+ border-bottom: 1px solid #b3b5b8;
+ border-top: none;
+ text-transform: uppercase;
+}
+
+table tr {
+ border-bottom: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+}
+
+.table-collection tr {
+ border-bottom: 1px solid #b3b5b8;
+ border-top: none;
+}
+
+.table-collection tbody tr:hover {
+ background-color: #f3f3f4;
+}
+
+.table-collection a {
+ color: #007bff;
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.table-collection a:hover {
+ text-decoration: none;
+}
+
+.table-collection .title-color {
+ color: #a70a19;
+ font-weight: 500;
+}
+
+.view-clone-btn {
+ font-size: 14px;
+ border-radius: 5px;
+ border: 1px solid #007aff;
+}
+
+.view-clone-btn:hover {
+ background-color: #187deb;
+ color: #fff;
+}
+
+.view-clone-btn:focus {
+ outline: none;
+}
+
+/* Coding Page */
+
+.session-details {
+ display: inline-flex;
+ flex-direction: column;
+}
+
+.header-menu {
+ vertical-align: text-top;
+}
+
+/**** Quick Access Classes ****/
+
+.padding-0-14 {
+ padding: 0 14px;
+}
+
+.margin-l-20 {
+ margin-left: 20px;
+}
+
+.margin-l-15 {
+ margin-left: 15px;
+}
+
+.margin-l-10 {
+ margin-left: 10px;
+}
+
+.text-center {
+ text-align: center;
}
\ No newline at end of file
diff --git a/src/containers/App.js b/src/containers/App.js
index cc9ca58..7aff957 100644
--- a/src/containers/App.js
+++ b/src/containers/App.js
@@ -1,12 +1,13 @@
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import "./App.css";
+import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
+import getMuiTheme from "material-ui/styles/getMuiTheme";
+import { createBrowserHistory } from "history";
import Login from "./pages/Login";
import Coding from "./pages/Coding";
import Home from './pages/Home';
-import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
-import getMuiTheme from "material-ui/styles/getMuiTheme";
-import createBrowserHistory from "history/createBrowserHistory";
+import Dashboard from './pages/Dashboard';
const muiTheme = getMuiTheme({
appBar: {
@@ -26,6 +27,7 @@ class App extends Component {
+
diff --git a/src/containers/pages/Coding.js b/src/containers/pages/Coding.js
index fb93aff..9f90b24 100644
--- a/src/containers/pages/Coding.js
+++ b/src/containers/pages/Coding.js
@@ -1,123 +1,344 @@
import React from "react";
-import CodeMirror from "react-codemirror";
-import Header from "../../components/Header";
-import { database } from "firebase";
+import random from "random-key";
+import Header from "../../components/Header/Header";
+import SideDrawer from '../../components/SideDrawer/SideDrawer';
+import Backdrop from '../../components/Backdrop/Backdrop';
+import { database } from "firebase/app";
+import { firebaseAuth } from "../../config/firebase-config";
import { logout } from "../../helpers/auth";
-
-import "codemirror/lib/codemirror";
-import "codemirror/lib/codemirror.css";
-import 'codemirror/mode/xml/xml';
-import "codemirror/mode/javascript/javascript";
-import "codemirror/theme/dracula.css";
-import 'codemirror/addon/edit/closetag';
-import 'codemirror/addon/edit/matchbrackets';
-import 'codemirror/addon/edit/closebrackets';
-import 'codemirror/addon/edit/matchtags';
-import 'codemirror/addon/edit/trailingspace';
-import 'codemirror/addon/hint/show-hint';
-import 'codemirror/addon/hint/show-hint.css';
-import 'codemirror/addon/hint/javascript-hint';
-import 'codemirror/addon/comment/comment';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faBars } from '@fortawesome/free-solid-svg-icons';
+import { faToggleOn, faToggleOff } from '@fortawesome/free-solid-svg-icons';
const appTokenKey = "appToken";
const sessionID = "sessionID";
+const creatorInfo = {};
export default class CodingPage extends React.Component {
constructor(props) {
super(props);
+ // favicon script
+ (function() {
+ var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
+ link.type = 'image/x-icon';
+ link.rel = 'shortcut icon';
+ link.href = '../favicon_CN.ico';
+ document.getElementsByTagName('head')[0].appendChild(link);
+ })();
+
const session_id = this.props.match.params.sessionid;
- // console.log(session_id);
+ // if appTokenKey is not found in localStorage,
+ // then redirect to login page
+ if (!localStorage.getItem(appTokenKey)) {
+ localStorage.setItem("sessionID", session_id);
+ this.props.history.push(`/login`);
+ return;
+ }
+
+ // setting initial state
this.state = {
- code: "Loading..."
- // firebaseUser: JSON.parse(localStorage.getItem("firebaseUser"))
+ key: random.generate(3), // for storing connected-users
+ isLoading: true,
+ readOnly: false,
+ isCreator: false,
+ sideDrawerOpen: false,
+ mode: 'xml',
+ code: "Loading...",
};
- // console.log("User:", this.state.firebaseUser);
+ // set 'readOnly' state and session title from database
+ database()
+ .ref(`code-sessions/${session_id}`)
+ .once("value")
+ .then(snapshot => {
+ let readOnly = snapshot.val().readOnly;
+ this.setState({ readOnly: readOnly });
+ // setting session title in the header
+ let sessionTitle = snapshot.val().title;
+ if(sessionTitle === undefined || sessionTitle === ""){
+ this.sessionTitle.value = "Untitled";
+ } else {
+ this.sessionTitle.value = sessionTitle;
+ }
+ })
+ .catch(e => {
+ // no session found corresponding to "sessionid" passed in the params
+ });
this.handleLogout = this.handleLogout.bind(this);
+ this.sessionTitleHandler = this.sessionTitleHandler.bind(this);
- // if appTokenKey is not found in localStorage,
- // then redirect to login page
- if (!localStorage.getItem(appTokenKey)) {
- localStorage.setItem("sessionID", session_id);
- this.props.history.push(`/login`);
- return;
- }
}
-
+
// setting initial state
state = {
+ sideDrawerOpen: false,
+ mode: 'xml',
code: "Loading...",
cursorPosition: {
line: 0,
ch: 0
- }
+ },
};
componentDidMount = () => {
+
const { params } = this.props.match;
- let self = this;
- database()
- .ref("/code-sessions/" + params.sessionid)
- .once("value")
- .then(snapshot => {
+ const session_id = params.sessionid;
- // trimmimg the Date() to remove unnecessary add-ons
- var createdOn = snapshot.val().createdon;
- var createdOnCompressed = createdOn.substring(0, 25);
- self.setState({ code: snapshot.val().content + "", createdon: createdOnCompressed }, () => {
- // fetching content from db and setting on the editor
- let content = snapshot.val().content;
- self.codemirror.getCodeMirror().setValue(content);
- // console.log(this.codemirror.getCodeMirror());
- });
-
- this.codeRef = database().ref("code-sessions/" + params.sessionid);
- // whenever changes are made:
- // "code" is updated from the db
- // cursor position is updated (changeCursorPos() is called)
- // code on the editor screen is updated from the db
- this.codeRef.on("value", function(snapshot) {
- self.setState({
- code: snapshot.val().content
+ // to fetch currently signed-in user
+ firebaseAuth().onAuthStateChanged(async (user) => {
+ try {
+ if (user) {
+
+ var creator_uid;
+
+ // fetch session creator's uid from database
+ // const creator_uid = await database()
+ await database()
+ .ref(`code-sessions/${session_id}/creator`)
+ .once("value")
+ .then(snapshot => {
+ var creatorData = snapshot.val();
+ creator_uid = creatorData.user_id;
+ creatorInfo.user_name = creatorData.user_name;
+ creatorInfo.user_photo = creatorData.user_photo;
+ // console.log(creatorData);
+ })
+ .catch(e => {
+ // no session found corresponding to "sessionid" passed in the params
});
- var currentCursorPos = self.state.cursorPosition;
- self.codemirror.getCodeMirror().setValue(snapshot.val().content);
- self.setState({ cursorPosition: currentCursorPos });
- self.changeCursorPos();
+
+ if (creator_uid === undefined ){
+ console.log("No Session Found!");
+ return;
+ }
+
+ // do not add user details to 'users-connected'
+ // if that user is the creator of session
+ if (user.uid !== creator_uid){
+
+ console.log("Current user is NOT the session creator");
+ console.log("User added to 'users-connected'");
+
+ var newUser;
+
+ // checking if user already present in database
+ await database()
+ .ref(`code-sessions/${session_id}/users-connected`)
+ .once("value")
+ .then(snapshot => {
+ snapshot.forEach(function(childSnapshot){
+ var userData = childSnapshot.val();
+ if(userData.user_id === user.uid){
+ newUser = false;
+ console.log("User details already present in the database");
+ }
+ });
+ if(newUser !== false){
+ newUser = true;
+ }
+ })
+ .catch(e => {
+ console.log(e);
+ });
+
+ // adding new user details in database
+ if(newUser === true){
+ console.log("User details added to the database");
+ await database()
+ .ref(`code-sessions/${session_id}/users-connected/user-` + this.state.key)
+ .set({
+ user_id: user.uid,
+ user_name: user.displayName,
+ user_email: user.email,
+ user_photo: user.photoURL
+ });
+ }
+
+ } else {
+ console.log("Current user is the session creator");
+ console.log("User NOT added to 'users-connected'");
+ this.setState({ isCreator: true });
+ }
+
+ // making the users-editing-state button dynamic
+ database()
+ .ref(`code-sessions/${session_id}`)
+ .on('value', snapshot => {
+ // console.log("readOnly changed!");
+ if(this.userEditingToggleBtn !== null){
+ let readOnlyState = snapshot.val().readOnly;
+ if(readOnlyState){
+ this.userEditingToggleBtn.innerHTML = "Editing: Disabled";
+ } else {
+ this.userEditingToggleBtn.innerHTML = "Editing: Enabled";
+ }
+ }
+ });
+
+ // prevent space as input in session-title
+ this.sessionTitle.addEventListener('keypress', function(event) {
+ let key = event.keyCode;
+ if (key === 32) {
+ event.preventDefault();
+ }
+ });
+
+ // setting session-tile readOnly state
+ if(this.state.isCreator){
+ if(this.sessionTitle !== null && this.sessionTitle !== undefined){
+ this.sessionTitle.readOnly = false;
+ }
+ } else {
+ if(this.sessionTitle !== null && this.sessionTitle !== undefined){
+ this.sessionTitle.readOnly = true;
+ }
+ }
+
+ // updating the session title dynamically
+ database()
+ .ref(`code-sessions/${session_id}/title`)
+ .on('value', snapshot => {
+ // console.log("session-title modified!");
+ if(this.sessionTitle !== null && this.sessionTitle !== undefined){
+ let sessionTitle = snapshot.val();
+ this.sessionTitle.value = sessionTitle;
+ }
+ });
+
+ } else {
+
+ console.log("Cannot fetch currently signed-in user!");
+ console.log("Signing out... please login again.");
+ if (localStorage.getItem(appTokenKey)) {
+ localStorage.setItem("sessionID", session_id);
+ logout().then(function () {
+ localStorage.removeItem(appTokenKey);
+ this.props.history.push("/login");
+ }.bind(this));
+ return;
+ }
+
+ }
+ } catch(error) {
+ console.log("Error in authentication:", error);
+ }
+ });
+
+ this.firepadRef = database().ref(`code-sessions/${session_id}/firepad`);
+
+ this.codemirror = window.CodeMirror(this.firepadDiv, {
+ mode: this.state.mode,
+ theme: "dracula",
+ lineNumbers: true,
+ lineWrapping: true,
+ autoCloseTags: true,
+ matchBrackets: true,
+ autoCloseBrackets: true,
+ matchTags: true,
+ showTrailingSpace: true,
+ extraKeys: {
+ 'Ctrl-Space' : 'autocomplete',
+ 'Cmd-/' : 'toggleComment',
+ 'Ctrl-/' : 'toggleComment'
+ },
+ });
+
+ this.firepad = window.Firepad.fromCodeMirror(this.firepadRef, this.codemirror, {
+ // richTextShortcuts: true,
+ // richTextToolbar: true,
+ // defaultText: "Loading...",
+ });
+
+ database()
+ .ref(`code-sessions/${session_id}/firepad/history`)
+ // .orderByKey()
+ .on("value", snapshot => {
+ // console.log("Number of revisions:", snapshot.numChildren());
+ let count = 0;
+ // keeping record (history) of last 150 changes in database
+ if(snapshot.numChildren() > 150){
+ let minCount = snapshot.numChildren() - 150;
+ snapshot.forEach(function(childSnapshot){
+ if(count === minCount){
+ return true;
+ }
+ // console.log("Deleted! " + count + " " + childSnapshot.key);
+ childSnapshot.ref.remove();
+ count++;
+ });
+ }
+
+ database()
+ .ref(`code-sessions/${session_id}/firepad/history`)
+ .once("value")
+ .then(snapshot => {
+ // console.log("New number of revisions:", snapshot.numChildren());
});
- })
- .catch(e => {
- // no session found corresponding to "sessionid" passed in the params
- self.codemirror.getCodeMirror().setValue("No Sessions Found!");
+
+ });
+
+ let self = this;
+ database()
+ .ref(`code-sessions/${session_id}`)
+ .once("value")
+ .then(snapshot => {
+
+ // trimmimg the Date() to remove unnecessary add-ons
+ var createdOn = snapshot.val().createdon;
+ var createdOnCompressed = createdOn.substring(0, 25);
+ self.setState({
+ code: snapshot.val().content + "",
+ createdon: createdOnCompressed
});
- };
- // updating cursor position
- changeCursorPos = () => {
- const { line, ch } = this.state.cursorPosition;
- this.codemirror.getCodeMirror().doc.setCursor(line, ch);
- };
+ this.firepad.on('ready', function() {
+ self.setState({isLoading: false});
+ if (self.firepad.isHistoryEmpty()) {
+ self.firepad.setText(self.state.code);
+ }
+ // self.firepad.setUserId(userId)
+ });
- // called whenever code is changed
- onChange = (newVal, change) => {
- // console.log(newVal, change);
- this.setState({
- cursorPosition: {
- line: this.codemirror.getCodeMirror().doc.getCursor().line,
- ch: this.codemirror.getCodeMirror().doc.getCursor().ch
+ // called whenever changes are made in editor
+ this.firepad.on('synced', function(isSynced) {
+
+ // console.log(firepad.getText());
+ let newVal = self.firepad.getText();
+
+ // updating data in database
+ let codeRef = database().ref(`code-sessions/${session_id}`);
+ codeRef.child("content").set(newVal);
+
+ });
+
+ // changing 'readOnly' state of editor
+ this.codeRef = database().ref(`code-sessions/${session_id}`);
+ this.codeRef.on("value", function(snapshot) {
+ // setting 'readOnly' option
+ if(self.state.isCreator){
+ self.codemirror.setOption("readOnly", false);
+ } else {
+ self.codemirror.setOption("readOnly", snapshot.val().readOnly);
}
- },
- () => {}
- );
- // updating data
- this.codeRef.child("content").set(newVal);
+ });
+
+ })
+ .catch(e => {
+ // no session found corresponding to "sessionid" passed in the params
+ this.firepad.dispose();
+ this.setState({isLoading: false});
+ this.sessionTitle.value = " ";
+ self.codemirror.setValue("No Session Found!");
+ });
+
};
- // sign-out functionality:
+ // sign-out functionality
handleLogout() {
logout().then(function () {
localStorage.removeItem(appTokenKey);
@@ -127,48 +348,165 @@ export default class CodingPage extends React.Component {
}.bind(this));
}
+ // sidebar toggle handler
+ drawerToggleClickHandler = () => {
+ this.setState((prevState) => {
+ return { sideDrawerOpen: !prevState.sideDrawerOpen };
+ });
+ };
+
+ // backdrop (dull background) handler
+ backdropClickHandler = () => {
+ this.setState({ sideDrawerOpen: false });
+ };
+
+ // read only toggle handler
+ toggleReadOnly = () => {
+ this.setState({
+ readOnly: !this.state.readOnly
+ }, () => this.codemirror.focus());
+ // updating readOnly in database
+ this.codeRef.child("readOnly").set(!this.state.readOnly);
+ };
+
+ // editor language handler
+ changeMode = (e) => {
+ var mode = e.target.value;
+ this.setState({
+ mode: mode
+ });
+ // setting new language mode in the editor
+ this.codemirror.setOption("mode", mode);
+ };
+
+ // executes whenever session-title is changed
+ sessionTitleHandler = (e) => {
+ let sessionTitle = e.target.value;
+ this.codeRef.child("title").set(sessionTitle);
+ };
+
render() {
+
+ let backdrop;
+ if (this.state.sideDrawerOpen) {
+ backdrop =
+ }
+
+ // readOnly toggle is only displayed when current user is the creator of session
+ let readOnlyToggle;
+ if (this.state.isCreator) {
+ readOnlyToggle =
+
+ }
+
+ // this button is only displayed to the users-connected (not the creator)
+ let userEditingToggle;
+ if (!this.state.isCreator) {
+ userEditingToggle =
+
+ }
+
+ // spinning loading icon
+ let spinningLogo;
+ if (this.state.isLoading) {
+ spinningLogo =
+
+ }
+
return (
+
+
+ Title:
+
+ (this.sessionTitle = r)}
+ className="session-title-input"
+ type="text"
+ readOnly={true}
+ placeholder="Enter title..."
+ defaultValue="Untitled"
+ minLength="1"
+ maxLength="30"
+ onChange={this.sessionTitleHandler} />
+
+ }
extras={
- {this.state.createdon
- ? `Created On: ${this.state.createdon}`
- : ""}
-
+
+
+
+ {this.state.createdon
+ ? `Created On: ${this.state.createdon}`
+ : ""}
+
+
+ {this.state.createdon
+ ? `Created By: ${creatorInfo.user_name}`
+ : ""}
+
+
+
+
+
+
+
+ { readOnlyToggle }
+ { userEditingToggle }
+
+
+
+
+
+
+
}
/>
-
- (this.codemirror = r)}
- className="code-mirror-container"
- value={this.state.code}
- onChange={this.onChange}
- options={{
- mode: "xml",
- theme: "dracula",
- lineNumbers: true,
- readOnly: false,
- autoCloseTags: true,
- matchBrackets: true,
- autoCloseBrackets: true,
- matchTags: true,
- showTrailingSpace: true,
- extraKeys: {
- 'Ctrl-Space' : 'autocomplete',
- 'Cmd-/' : 'toggleComment',
- 'Ctrl-/' : 'toggleComment'
- }
- }}
- />
-
+
+ { spinningLogo }
+
+
+ { backdrop }
+
+ (this.firepadDiv = r)}>
+
);
}
-}
\ No newline at end of file
+}
+
+// exporting creator info object
+export {creatorInfo};
\ No newline at end of file
diff --git a/src/containers/pages/Dashboard.js b/src/containers/pages/Dashboard.js
new file mode 100644
index 0000000..4c87756
--- /dev/null
+++ b/src/containers/pages/Dashboard.js
@@ -0,0 +1,324 @@
+import React from "react";
+import Header from "../../components/Header/Header";
+import { database } from "firebase/app";
+import { firebaseAuth } from "../../config/firebase-config";
+import { logout } from "../../helpers/auth";
+
+const appTokenKey = "appToken";
+const sessionID = "sessionID";
+const userSessionsList = [];
+const sessionClonesList = [];
+const userDisplayName = {};
+
+export default class Dashboard extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ helper: true,
+ isLoadingUserSession: true,
+ viewClonesHelpText: false,
+ isLoadingSessionClones: false,
+ noSessionFoundText: false,
+ noCloneFoundText: false,
+ };
+
+ if (localStorage.getItem(sessionID)){
+ localStorage.removeItem(sessionID);
+ }
+
+ if (!localStorage.getItem(appTokenKey)) {
+ this.props.history.push(`/login`);
+ return;
+ }
+
+ this.handleLogout = this.handleLogout.bind(this);
+ this.viewClonesHandler = this.viewClonesHandler.bind(this);
+ }
+
+ componentDidMount = () => {
+
+ // to fetch currently signed-in user
+ firebaseAuth().onAuthStateChanged(user => {
+ try {
+ if (user) {
+
+ // storing current user's name
+ userDisplayName.name = user.displayName;
+
+ // to reset/hide the session clones list whenever dashboard page is opened
+ sessionClonesList.splice(0, sessionClonesList.length);
+
+ // database().ref(`/${session_id}/creator`).orderByChild("user_id").equalTo(user.uid).on('value', function (snapshot) {});
+
+ database()
+ .ref(`code-sessions/`)
+ .once("value")
+ .then(snapshot => {
+ // console.log("Searching for all sessions created by current user...");
+ userSessionsList.splice(0, userSessionsList.length);
+ let found = false;
+ let self = this;
+ let i = 0;
+
+ snapshot.forEach(function(childSnapshot){
+
+ let creator_id = childSnapshot.child("creator").child("user_id").val();
+ let sessionsTitle = childSnapshot.child("title").val();
+ let createdOn = childSnapshot.child("createdon").val();
+ let createdOnCompressed = createdOn.substring(0, 24);
+
+ if(creator_id === user.uid) {
+
+ self.setState({
+ isLoadingUserSession: false,
+ viewClonesHelpText: true,
+ });
+
+ found = true;
+
+ let sessionid = childSnapshot.key;
+ // console.log(`User session id: ${sessionid}`);
+
+ userSessionsList.push(
+
+ |
+
+ {sessionid}
+
+ |
+
+
+ { sessionsTitle }
+
+ |
+ { createdOnCompressed } |
+
+
+ |
+
+ );
+ i++;
+
+ self.setState(self.state);
+
+ }
+ });
+
+ if(!found){
+ this.setState({
+ isLoadingUserSession: false,
+ noSessionFoundText: true,
+ });
+ // console.log("No clones found!");
+ }
+
+ // this.setState(this.state);
+ });
+
+ } else {
+
+ console.log("Cannot fetch currently signed-in user!");
+ console.log("Signing out... please login again.");
+ if (localStorage.getItem(appTokenKey)) {
+ // localStorage.setItem("sessionID", session_id);
+ logout().then(function () {
+ localStorage.removeItem(appTokenKey);
+ this.props.history.push("/login");
+ }.bind(this));
+ return;
+ }
+
+ }
+ } catch(error) {
+ console.log("Error in authentication:", error);
+ }
+ });
+ }
+
+ viewClonesHandler = (e) => {
+
+ this.setState({
+ viewClonesHelpText: false,
+ isLoadingSessionClones: true,
+ noCloneFoundText: false,
+ });
+
+ sessionClonesList.splice(0, sessionClonesList.length);
+
+ let correspondingSessionID = e.currentTarget.value;
+ // console.log(`correspondingSessionID: ${correspondingSessionID}`);
+
+ database()
+ .ref(`code-sessions/`)
+ .once("value")
+ .then(snapshot => {
+
+ // sessionClonesList.splice(0, sessionClonesList.length);
+ let found = false;
+ let self = this;
+ let i = 0;
+
+ snapshot.forEach(function(childSnapshot){
+
+ let clonedFromSessionID = childSnapshot.child("clonedFromSession").val();
+ let userName = childSnapshot.child("creator").child("user_name").val();
+ let userEmail = childSnapshot.child("creator").child("user_email").val();
+ let createdOn = childSnapshot.child("createdon").val();
+ let createdOnCompressed = createdOn.substring(0, 24);
+
+ if(clonedFromSessionID === correspondingSessionID) {
+ self.setState({isLoadingSessionClones: false});
+ found = true;
+ let cloneSesssionID = childSnapshot.key;
+ // console.log(`cloneSesssionID: ${cloneSesssionID}`);
+
+ sessionClonesList.push(
+
+ |
+
+ {cloneSesssionID}
+
+ |
+
+ { userName }
+ |
+ { userEmail } |
+ { createdOnCompressed } |
+
+ );
+ i++;
+
+ self.setState(self.state);
+ }
+
+ });
+
+ if(!found){
+ this.setState({
+ isLoadingSessionClones: false,
+ noCloneFoundText: true,
+ });
+ // console.log("No clones found!");
+ }
+
+ this.setState(this.state);
+
+ });
+ };
+
+ // sign-out functionality
+ handleLogout() {
+ logout().then(function () {
+ localStorage.removeItem(appTokenKey);
+ localStorage.removeItem(sessionID);
+ this.props.history.push("/login");
+ console.log("User signed-out from firebase.");
+ }.bind(this));
+ }
+
+ render() {
+
+ let loadingTextUserSession;
+ if(this.state.isLoadingUserSession){
+ loadingTextUserSession = Loading...;
+ }
+
+ let loadingTextSessionClones;
+ if(this.state.isLoadingSessionClones){
+ loadingTextSessionClones = Loading...;
+ }
+
+ let viewClonesHelpText;
+ if(!this.state.isLoadingUserSession && this.state.viewClonesHelpText){
+ viewClonesHelpText =
+ (Click on the 'View Clones' button
+ corresponding to a session ID above);
+ }
+
+ let noCloneFoundText;
+ if(this.state.noCloneFoundText){
+ noCloneFoundText =
+ No Clones Found!;
+ }
+
+ let noSessionFoundText;
+ if(this.state.noSessionFoundText){
+ noSessionFoundText =
+ No Sessions Found!;
+ }
+
+ let usersSessionHeading;
+ let usersCloneHeading;
+ if(userDisplayName.name !== undefined){
+ let name = userDisplayName.name;
+ usersSessionHeading = name + "'s sessions";
+ usersCloneHeading = "Session clones";
+ }
+
+ return (
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/containers/pages/Home.js b/src/containers/pages/Home.js
index a7d4665..a5bd9c5 100644
--- a/src/containers/pages/Home.js
+++ b/src/containers/pages/Home.js
@@ -1,7 +1,8 @@
import React from "react";
import random from "random-key";
-import Header from "../../components/Header";
-import { database } from "firebase";
+import Header from "../../components/Header/Header";
+import { database } from "firebase/app";
+import { firebaseAuth } from "../../config/firebase-config";
import { logout } from "../../helpers/auth";
const appTokenKey = "appToken";
@@ -12,11 +13,6 @@ export default class HomePage extends React.Component {
constructor(props) {
super(props);
- // this.state = {
- // //firebaseUser: JSON.parse(localStorage.getItem("firebaseUser"))
- // };
-
- //console.log("User:", this.state.firebaseUser);
this.handleLogout = this.handleLogout.bind(this);
if(localStorage.getItem(sessionID)){
@@ -44,18 +40,72 @@ export default class HomePage extends React.Component {
});
};
- // when new session is created (Share Code button is clicked)
+ // when new session is created ('Share Code' button is clicked)
onNewGround = () => {
- database()
- .ref("code-sessions/" + this.state.key)
- .set({
- content: " I ♥ Coding!
",
- createdon: Date()
- });
- this.props.history.push("/home/" + this.state.key);
+
+ // to fetch currently signed-in user
+ firebaseAuth().onAuthStateChanged(user => {
+ // firebaseAuth().onIdTokenChanged(user => {
+ try {
+ if (user) {
+
+ database()
+ .ref("code-sessions/" + this.state.key)
+ .set({
+ content: " I ♥ Coding!
",
+ createdon: Date(),
+ title: "Untitled",
+ readOnly: false, // by default 'false'
+ });
+
+ // adding details of the user to the database
+ database()
+ .ref("code-sessions/" + this.state.key + "/creator")
+ .set({
+ user_id: user.uid,
+ user_name: user.displayName,
+ user_email: user.email,
+ user_photo: user.photoURL
+ });
+
+ this.props.history.push("/home/" + this.state.key);
+
+ } else {
+ console.log("Cannot fetch currently signed-in user!");
+ console.log("Signing out... please login again.");
+ this.handleLogout();
+ }
+ } catch(error) {
+ console.log("Error in authentication:", error);
+ }
+ });
+
+ };
+
+ // when 'Dashboard' button is clicked
+ goToDashboard = () => {
+
+ // to fetch currently signed-in user
+ firebaseAuth().onAuthStateChanged(user => {
+ // firebaseAuth().onIdTokenChanged(user => {
+ try {
+ if (user) {
+
+ this.props.history.push("/dashboard");
+
+ } else {
+ console.log("Cannot fetch currently signed-in user!");
+ console.log("Signing out... please login again.");
+ this.handleLogout();
+ }
+ } catch(error) {
+ console.log("Error in authentication:", error);
+ }
+ });
+
};
- // sign-out functionality:
+ // sign-out functionality
handleLogout() {
logout().then(function () {
localStorage.removeItem(appTokenKey);
@@ -69,13 +119,17 @@ export default class HomePage extends React.Component {
return (