├── development
├── src
│ ├── data
│ │ └── dummyData.js
│ ├── composables
│ │ ├── dummyFunction.js
│ │ ├── accountVerifiedFunc.js
│ │ ├── changeTheme.js
│ │ ├── verifySignedIn.js
│ │ ├── RequestVerificationLink.js
│ │ ├── signOutFunction.js
│ │ ├── peerIdGenerator.js
│ │ ├── sendAccountResetEmail.js
│ │ ├── sendPasswordResetFunction.js
│ │ ├── emailPasswordValidator.js
│ │ ├── sessionContext.js
│ │ ├── sessionManagementFunction.js
│ │ ├── userLoginFunction.js
│ │ ├── signalingServer.js
│ │ ├── firebaseConfig
│ │ │ └── config.js
│ │ ├── authGoogleSigninPoppup.js
│ │ ├── authGithubSigninPopup.js
│ │ ├── authSignupFunction.js
│ │ ├── signupFormValidation.js
│ │ ├── tabContext.js
│ │ └── dbService.js
│ ├── app
│ │ ├── globals.css
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── Nohemi-VF-BF6438cc58ad63d.ttf
│ │ │ ├── Nohemi-Bold-BF6438cc5812315.otf
│ │ │ ├── Nohemi-Bold-BF6438cc587b5b5.ttf
│ │ │ ├── Nohemi-Thin-BF6438cc577ef3b.otf
│ │ │ ├── Nohemi-Thin-BF6438cc5896c67.ttf
│ │ │ ├── Nohemi-Black-BF6438cc565e67b.woff
│ │ │ ├── Nohemi-Black-BF6438cc58744d4.ttf
│ │ │ ├── Nohemi-Black-BF6438cc5874bd2.otf
│ │ │ ├── Nohemi-Bold-BF6438cc577b524.woff
│ │ │ ├── Nohemi-Light-BF6438cc5702321.woff
│ │ │ ├── Nohemi-Light-BF6438cc583f70b.otf
│ │ │ ├── Nohemi-Light-BF6438cc5899919.ttf
│ │ │ ├── Nohemi-Medium-BF6438cc57ddecd.woff
│ │ │ ├── Nohemi-Medium-BF6438cc581a509.otf
│ │ │ ├── Nohemi-Medium-BF6438cc5883899.ttf
│ │ │ ├── Nohemi-Regular-BF6438cc4d0e493.ttf
│ │ │ ├── Nohemi-Regular-BF6438cc58b98fc.otf
│ │ │ ├── Nohemi-Thin-BF6438cc57e2011.woff
│ │ │ ├── Nohemi-ExtraBold-BF6438cc5881baf.ttf
│ │ │ ├── Nohemi-ExtraBold-BF6438cc58a4c3c.otf
│ │ │ ├── Nohemi-Regular-BF6438cc579d934.woff
│ │ │ ├── Nohemi-SemiBold-BF6438cc57db2ff.woff
│ │ │ ├── Nohemi-SemiBold-BF6438cc588a48a.ttf
│ │ │ ├── Nohemi-SemiBold-BF6438cc588b5e5.otf
│ │ │ ├── Nohemi-ExtraBold-BF6438cc5761ae2.woff
│ │ │ ├── Nohemi-ExtraLight-BF6438cc57e06d5.otf
│ │ │ ├── Nohemi-ExtraLight-BF6438cc581502c.woff
│ │ │ └── Nohemi-ExtraLight-BF6438cc58a2634.ttf
│ │ ├── layout.js
│ │ ├── localFont.js
│ │ └── page.js
│ └── components
│ │ ├── onboardingHeader.js
│ │ ├── modal.js
│ │ ├── navbar_components
│ │ ├── sidebar_components
│ │ │ ├── sideTopNavControl.js
│ │ │ ├── sideBarNavControl.js
│ │ │ └── sideBottomNavControl.js
│ │ ├── authNavControls_comp.js
│ │ ├── tabbar_components
│ │ │ ├── tabBarControls_comp.js
│ │ │ └── tabBarItem.js
│ │ └── editorNavbar_comp.js
│ │ ├── sessionManagerComp.js
│ │ ├── welcome_comp.js
│ │ ├── errorModal_comp.js
│ │ ├── session_comp.js
│ │ ├── VerificationSuccessful_comp.js
│ │ ├── codingEditor.js
│ │ ├── onboarding_comp.js
│ │ ├── joinSession_Comp.js
│ │ ├── VerificationOverlay.js
│ │ ├── openTabModal_comp.js
│ │ ├── ForgotPassword.js
│ │ ├── changeEmailOverlay_comp.js
│ │ ├── passwordToggleFunction.js
│ │ ├── peerId_comp.js
│ │ ├── PeerOverlay_comp.js
│ │ ├── collab_comp.js
│ │ ├── userLogin_comp.js
│ │ ├── webcam_comp.js
│ │ └── signup_comp.js
├── .eslintrc.json
├── postcss.config.js
├── jsconfig.json
├── public
│ ├── assets
│ │ ├── openTabIcons
│ │ │ ├── CSS3.png
│ │ │ ├── HTML.png
│ │ │ ├── Javascript.png
│ │ │ └── users.svg
│ │ ├── sideTopNavBar
│ │ │ ├── add.png
│ │ │ ├── code.png
│ │ │ └── user.png
│ │ ├── codeEditorIcons
│ │ │ ├── CSS3.png
│ │ │ ├── Group.png
│ │ │ ├── symbol.png
│ │ │ └── vuesax
│ │ │ │ └── bold
│ │ │ │ ├── Vector.png
│ │ │ │ ├── send-2.png
│ │ │ │ ├── Vector-1.png
│ │ │ │ ├── Vector-2.png
│ │ │ │ ├── Vector-3.png
│ │ │ │ ├── Vector-4.png
│ │ │ │ ├── element-4.png
│ │ │ │ ├── direct-inbox.png
│ │ │ │ └── notification.png
│ │ ├── onboardingIcons
│ │ │ ├── moon.png
│ │ │ ├── sun.png
│ │ │ ├── github.png
│ │ │ ├── google.png
│ │ │ ├── arrow_dark.png
│ │ │ ├── arrow_light.png
│ │ │ ├── close_black.png
│ │ │ ├── close_light.png
│ │ │ ├── github_black.png
│ │ │ ├── closecircledark.png
│ │ │ ├── closecirclelight.png
│ │ │ ├── github_darkMode.png
│ │ │ ├── github_lightMode.png
│ │ │ ├── eyes.js
│ │ │ ├── eye_slash.js
│ │ │ ├── eye.svg
│ │ │ ├── closecirclelight.svg
│ │ │ └── eye-slash.svg
│ │ ├── authNavBarControls
│ │ │ ├── peers.png
│ │ │ ├── save-1.png
│ │ │ ├── save.png
│ │ │ ├── peers-1.png
│ │ │ └── peers-2.png
│ │ ├── forgotPasswordForm
│ │ │ └── close_white.png
│ │ ├── sideBottomNavControls
│ │ │ ├── logout.png
│ │ │ └── settings.png
│ │ ├── githubGoogleError
│ │ │ ├── error.svg
│ │ │ └── closecircle.svg
│ │ └── dashboard
│ │ │ └── welcome_comp.svg
│ ├── vercel.svg
│ └── next.svg
├── next.config.js
├── .gitignore
├── tailwind.config.js
├── server.js
└── package.json
├── design
├── carai onboarding dark mode screens
│ ├── .DS_Store
│ ├── Splash Page.png
│ ├── Splash Page-1.png
│ ├── Splash Page-2.png
│ ├── Splash Page-3.png
│ ├── Splash Page-4.png
│ ├── Splash Page-5.png
│ └── Splash Page-6.png
├── carai editor screens dark mode
│ ├── Frame 624619.png
│ ├── Frame 624620.png
│ ├── Frame 624621.png
│ ├── HTML Screen-1.png
│ ├── HTML Screen-2.png
│ ├── HTML Screen-3.png
│ ├── HTML Screen-4.png
│ ├── HTML Screen-5.png
│ ├── HTML Screen-6.png
│ ├── HTML Screen-7.png
│ ├── HTML Screen-8.png
│ ├── HTML Screen.png
│ ├── Splash Page-1.png
│ ├── Splash Page.png
│ ├── Signed User Dashboard - Empty State.png
│ ├── Signed User Dashboard - Empty State 1.png
│ ├── Signed User Dashboard - Empty State 2.png
│ ├── Signed User Dashboard - Empty State 3.png
│ ├── Signed User Dashboard - Empty State-1.png
│ └── Signed User Dashboard - Empty State-2.png
└── carai onboarding light mode screens
│ ├── Onboarding Screen.png
│ ├── Onboarding Screen-1.png
│ ├── Onboarding Screen-2.png
│ ├── Onboarding Screen-3.png
│ ├── Onboarding Screen-4.png
│ └── Onboarding Screen-5.png
└── README.md
/development/src/data/dummyData.js:
--------------------------------------------------------------------------------
1 | // dummy data
2 |
--------------------------------------------------------------------------------
/development/src/composables/dummyFunction.js:
--------------------------------------------------------------------------------
1 | // Dummy function
2 |
--------------------------------------------------------------------------------
/development/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/babel","next/core-web-vitals"]
3 | }
4 |
--------------------------------------------------------------------------------
/development/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/development/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/favicon.ico
--------------------------------------------------------------------------------
/development/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/development/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"],
5 |
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/development/public/assets/openTabIcons/CSS3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/openTabIcons/CSS3.png
--------------------------------------------------------------------------------
/development/public/assets/openTabIcons/HTML.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/openTabIcons/HTML.png
--------------------------------------------------------------------------------
/development/public/assets/sideTopNavBar/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/sideTopNavBar/add.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/CSS3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/CSS3.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/moon.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/sun.png
--------------------------------------------------------------------------------
/development/public/assets/sideTopNavBar/code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/sideTopNavBar/code.png
--------------------------------------------------------------------------------
/development/public/assets/sideTopNavBar/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/sideTopNavBar/user.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/.DS_Store
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/Group.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/symbol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/symbol.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/github.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/google.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Frame 624619.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Frame 624619.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Frame 624620.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Frame 624620.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Frame 624621.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Frame 624621.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-1.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-2.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-3.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-4.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-5.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-6.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-7.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen-8.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/HTML Screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/HTML Screen.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Splash Page-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Splash Page-1.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Splash Page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Splash Page.png
--------------------------------------------------------------------------------
/development/public/assets/authNavBarControls/peers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/authNavBarControls/peers.png
--------------------------------------------------------------------------------
/development/public/assets/authNavBarControls/save-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/authNavBarControls/save-1.png
--------------------------------------------------------------------------------
/development/public/assets/authNavBarControls/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/authNavBarControls/save.png
--------------------------------------------------------------------------------
/development/public/assets/openTabIcons/Javascript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/openTabIcons/Javascript.png
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-VF-BF6438cc58ad63d.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-VF-BF6438cc58ad63d.ttf
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page.png
--------------------------------------------------------------------------------
/development/public/assets/authNavBarControls/peers-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/authNavBarControls/peers-1.png
--------------------------------------------------------------------------------
/development/public/assets/authNavBarControls/peers-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/authNavBarControls/peers-2.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/arrow_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/arrow_dark.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/arrow_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/arrow_light.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/close_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/close_black.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/close_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/close_light.png
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Bold-BF6438cc5812315.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Bold-BF6438cc5812315.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Bold-BF6438cc587b5b5.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Bold-BF6438cc587b5b5.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Thin-BF6438cc577ef3b.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Thin-BF6438cc577ef3b.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Thin-BF6438cc5896c67.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Thin-BF6438cc5896c67.ttf
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-1.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-2.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-3.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-4.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-5.png
--------------------------------------------------------------------------------
/design/carai onboarding dark mode screens/Splash Page-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding dark mode screens/Splash Page-6.png
--------------------------------------------------------------------------------
/development/public/assets/forgotPasswordForm/close_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/forgotPasswordForm/close_white.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/github_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/github_black.png
--------------------------------------------------------------------------------
/development/public/assets/sideBottomNavControls/logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/sideBottomNavControls/logout.png
--------------------------------------------------------------------------------
/development/public/assets/sideBottomNavControls/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/sideBottomNavControls/settings.png
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Black-BF6438cc565e67b.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Black-BF6438cc565e67b.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Black-BF6438cc58744d4.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Black-BF6438cc58744d4.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Black-BF6438cc5874bd2.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Black-BF6438cc5874bd2.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Bold-BF6438cc577b524.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Bold-BF6438cc577b524.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Light-BF6438cc5702321.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Light-BF6438cc5702321.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Light-BF6438cc583f70b.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Light-BF6438cc583f70b.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Light-BF6438cc5899919.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Light-BF6438cc5899919.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Medium-BF6438cc57ddecd.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Medium-BF6438cc57ddecd.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Medium-BF6438cc581a509.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Medium-BF6438cc581a509.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Medium-BF6438cc5883899.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Medium-BF6438cc5883899.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Regular-BF6438cc4d0e493.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Regular-BF6438cc4d0e493.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Regular-BF6438cc58b98fc.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Regular-BF6438cc58b98fc.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Thin-BF6438cc57e2011.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Thin-BF6438cc57e2011.woff
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/closecircledark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/closecircledark.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/closecirclelight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/closecirclelight.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/github_darkMode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/github_darkMode.png
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/github_lightMode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/onboardingIcons/github_lightMode.png
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc5881baf.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc5881baf.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc58a4c3c.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc58a4c3c.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-Regular-BF6438cc579d934.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-Regular-BF6438cc579d934.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-SemiBold-BF6438cc57db2ff.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-SemiBold-BF6438cc57db2ff.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-SemiBold-BF6438cc588a48a.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-SemiBold-BF6438cc588a48a.ttf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-SemiBold-BF6438cc588b5e5.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-SemiBold-BF6438cc588b5e5.otf
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/Vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/Vector.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/send-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/send-2.png
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc5761ae2.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraBold-BF6438cc5761ae2.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc57e06d5.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc57e06d5.otf
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc581502c.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc581502c.woff
--------------------------------------------------------------------------------
/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc58a2634.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/src/app/fonts/Nohemi-ExtraLight-BF6438cc58a2634.ttf
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen-1.png
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen-2.png
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen-3.png
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen-4.png
--------------------------------------------------------------------------------
/design/carai onboarding light mode screens/Onboarding Screen-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai onboarding light mode screens/Onboarding Screen-5.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/Vector-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/Vector-1.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/Vector-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/Vector-2.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/Vector-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/Vector-3.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/Vector-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/Vector-4.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/element-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/element-4.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/direct-inbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/direct-inbox.png
--------------------------------------------------------------------------------
/development/public/assets/codeEditorIcons/vuesax/bold/notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/development/public/assets/codeEditorIcons/vuesax/bold/notification.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State 1.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State 2.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State 3.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State-1.png
--------------------------------------------------------------------------------
/design/carai editor screens dark mode/Signed User Dashboard - Empty State-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/world-wide-techies/P2PCoder/HEAD/design/carai editor screens dark mode/Signed User Dashboard - Empty State-2.png
--------------------------------------------------------------------------------
/development/src/composables/accountVerifiedFunc.js:
--------------------------------------------------------------------------------
1 | // function IsAccountVerified(currentUser) {
2 | // if (currentUser) {
3 | // return currentUser.emailVerified;
4 | // }
5 | // return false;
6 | // }
7 |
8 | // export { IsAccountVerified };
9 |
--------------------------------------------------------------------------------
/development/src/composables/changeTheme.js:
--------------------------------------------------------------------------------
1 | function handleThemeChange(theme) {
2 | if (theme == 'light') {
3 | localStorage.setItem('appTheme', 'dark');
4 | } else {
5 | localStorage.setItem('appTheme', 'light');
6 | }
7 | }
8 |
9 | export { handleThemeChange };
10 |
--------------------------------------------------------------------------------
/development/src/composables/verifySignedIn.js:
--------------------------------------------------------------------------------
1 | import { appAuth } from "./firebaseConfig/config";
2 |
3 | function isUserSignedIn() {
4 | const user = appAuth.currentUser;
5 | if (user && user.emailVerified) {
6 | return true;
7 | } else {
8 | return false;
9 | }
10 | }
11 |
12 | export { isUserSignedIn };
13 |
--------------------------------------------------------------------------------
/development/next.config.js:
--------------------------------------------------------------------------------
1 | const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
2 | /** @type {import('next').NextConfig} */
3 | const nextConfig = {
4 | webpack: (config, { dev, isServer }) => {
5 | config.plugins.push(new MonacoWebpackPlugin());
6 | return config;
7 | },
8 | };
9 |
10 | module.exports = nextConfig;
11 |
--------------------------------------------------------------------------------
/development/src/composables/RequestVerificationLink.js:
--------------------------------------------------------------------------------
1 | function startCounter() {
2 | let counter = 120;
3 | const timer = setInterval(() => {
4 | if (counter == 0) {
5 | clearInterval(timer);
6 | callback(true);
7 | } else {
8 | counter--;
9 | callback(false);
10 | }
11 | }, 1000);
12 | }
13 | export { startCounter };
14 |
--------------------------------------------------------------------------------
/development/src/composables/signOutFunction.js:
--------------------------------------------------------------------------------
1 | import { signOut } from "firebase/auth";
2 | import { appAuth } from "./firebaseConfig/config";
3 |
4 | async function handleLogout() {
5 | try {
6 | await signOut(appAuth);
7 | return {success: true};
8 | } catch (error) {
9 | console.log(error.message);
10 | }
11 | }
12 |
13 | export { handleLogout };
14 |
--------------------------------------------------------------------------------
/development/src/components/onboardingHeader.js:
--------------------------------------------------------------------------------
1 | export const OnboardingHeader = ({ h1, p }) => {
2 | return (
3 |
4 |
5 | {h1}
6 |
7 |
8 | {p}
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/development/src/composables/peerIdGenerator.js:
--------------------------------------------------------------------------------
1 | function generatePeerIdCharacter() {
2 | let characters =
3 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4 |
5 | let peerId = "";
6 |
7 | for (let i = 0; i < 10; i++)
8 | peerId += characters.charAt(Math.floor(Math.random() * characters.length));
9 |
10 | return peerId;
11 | }
12 |
13 | export { generatePeerIdCharacter };
14 |
--------------------------------------------------------------------------------
/development/src/composables/sendAccountResetEmail.js:
--------------------------------------------------------------------------------
1 | import { sendPasswordResetEmail } from 'firebase/auth';
2 | import { appAuth } from './firebaseConfig/config';
3 |
4 | async function sendAccountResetEmail() {
5 | try {
6 | await sendPasswordResetEmail(appAuth, email);
7 | return true;
8 | } catch (error) {
9 | const errorCode = error.code;
10 | const errorMessage = error.message;
11 | return errorMessage;
12 | }
13 | }
14 | export { sendAccountResetEmail };
15 |
--------------------------------------------------------------------------------
/development/src/composables/sendPasswordResetFunction.js:
--------------------------------------------------------------------------------
1 | import { sendPasswordResetEmail } from "firebase/auth";
2 | import { appAuth } from "./firebaseConfig/config";
3 |
4 | async function resetPassword(emailAddress) {
5 | try {
6 | await sendPasswordResetEmail(appAuth, emailAddress);
7 | return "Password reset email sent!";
8 | } catch (error) {
9 | const errorCode = error.code;
10 | const errorMessage = error.message;
11 | throw error;
12 | }
13 | }
14 |
15 | export { resetPassword };
16 |
--------------------------------------------------------------------------------
/development/src/composables/emailPasswordValidator.js:
--------------------------------------------------------------------------------
1 | function emailValidator(email) {
2 | var regxEmail = /^([a-zA-Z0-9\._]+)@([a-zA-Z])+.([a-zA-Z]+)(\.[a-zA-Z]+)?$/;
3 |
4 | return regxEmail.test(email) ? true : "Please enter a valid email";
5 | }
6 |
7 | function passwordValidator(password) {
8 | var regxPassword =
9 | /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?!.*\s).{8,15}$/;
10 |
11 | return regxPassword.test(password) ? true : "Please enter a valid password";
12 | }
13 |
14 | export { emailValidator, passwordValidator };
15 |
--------------------------------------------------------------------------------
/development/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | # testing
8 | /coverage
9 |
10 | # next.js
11 | /.next/
12 | /out/
13 |
14 | # production
15 | /build
16 |
17 | # Folders
18 | /src/app/tests/
19 |
20 | # misc
21 | .DS_Store
22 | *.pem
23 |
24 | # debug
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
29 | # local env files
30 | .env*.local
31 | .env
32 |
33 | # vercel
34 | .vercel
35 |
36 | # typescript
37 | *.tsbuildinfo
38 | next-env.d.ts
39 |
--------------------------------------------------------------------------------
/development/public/assets/githubGoogleError/error.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/development/src/composables/sessionContext.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useState } from 'react';
3 | import { useContext, createContext } from 'react';
4 |
5 | export const SessionContext = createContext();
6 |
7 | export const SessionContextProvider = ({ children }) => {
8 | const [sessionData, setSessionData] = useState({});
9 |
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | export const useSessionContext = () => {
18 | return useContext(SessionContext);
19 | };
20 |
--------------------------------------------------------------------------------
/development/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/development/src/composables/sessionManagementFunction.js:
--------------------------------------------------------------------------------
1 | function initializeTimer(userJoinTime) {
2 | const timer = setInterval(() => {
3 | const currentTime = new Date();
4 | const currentMinutes = currentTime.getMinutes();
5 | const currentSeconds = currentTime.getSeconds();
6 |
7 | const [joinMinutes, joinSeconds] = userJoinTime.split(":").map(Number);
8 |
9 | const timeDifference =
10 | currentMinutes * 60 + currentSeconds - (joinMinutes * 60 + joinSeconds);
11 |
12 | if (timeDifference >= 59 * 60 + 59) {
13 | clearInterval(timer);
14 | endSession();
15 | }
16 | }, 1000);
17 | }
18 |
--------------------------------------------------------------------------------
/development/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: 'class',
4 | content: [
5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}',
7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}',
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
13 | 'gradient-conic':
14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
15 | },
16 | fontFamily: {
17 | nohemi: ['var(--font-nohemi)'],
18 | },
19 | },
20 | },
21 | plugins: [],
22 | };
23 |
--------------------------------------------------------------------------------
/development/src/components/modal.js:
--------------------------------------------------------------------------------
1 | import { Dialog } from "@headlessui/react";
2 | import { useRef } from "react";
3 |
4 | export function Modal({ onClose = () => {}, children }) {
5 | let overlayRef = useRef();
6 |
7 | return (
8 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/development/src/app/layout.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import './globals.css';
4 | import { nohemi } from '@/app/localFont';
5 | import { ThemeProvider } from 'next-themes';
6 | import { TabProvider } from '@/composables/tabContext';
7 | import { SessionContextProvider } from '@/composables/sessionContext';
8 |
9 | export default function RootLayout({ children }) {
10 | return (
11 |
12 |
13 |
14 |
15 | {children}
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/development/src/composables/userLoginFunction.js:
--------------------------------------------------------------------------------
1 | import { signInWithEmailAndPassword } from "firebase/auth";
2 | import { appAuth } from "./firebaseConfig/config";
3 |
4 | async function UserLogin(emailAddress, password) {
5 | try {
6 | const userInfo = await signInWithEmailAndPassword(
7 | appAuth,
8 | emailAddress,
9 | password
10 | );
11 | const user = userInfo.user;
12 | if (user && !user.emailVerified) {
13 | return { loggedIn: false, message: "Please verify your email first" };
14 | }
15 | if (user) {
16 | return { loggedIn: true, message: user };
17 | }
18 | } catch (error) {
19 | throw error;
20 | }
21 | }
22 |
23 | export default UserLogin;
24 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/sidebar_components/sideTopNavControl.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import Image from 'next/image';
3 | import Link from 'next/link';
4 |
5 | function SideTopNavControl() {
6 | return (
7 |
8 |
11 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default SideTopNavControl;
23 |
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/eyes.js:
--------------------------------------------------------------------------------
1 | const eyes = () => (
2 |
21 | );
22 |
23 | export { eyes };
24 |
--------------------------------------------------------------------------------
/development/src/composables/signalingServer.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const http = require('http').createServer(app);
4 | const io = require('socket.io')(http);
5 |
6 | io.on('connection', (socket) => {
7 | console.log('A client connected');
8 |
9 | socket.on('message', (message) => {
10 | console.log('Received signaling message:', message);
11 |
12 | });
13 |
14 | socket.on('disconnect', () => {
15 | console.log('A client disconnected');
16 |
17 | });
18 | });
19 |
20 | app.get("/", (req, res) => {
21 | res.send('Server is running.')
22 | })
23 |
24 | const port = 3000;
25 | http.listen(port, () => {
26 | console.log(`Signaling server listening on port ${port}`);
27 | });
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/eye_slash.js:
--------------------------------------------------------------------------------
1 | const eyeClose = () => (
2 |
16 | );
17 |
18 | export { eyeClose };
19 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/authNavControls_comp.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Image from "next/image";
3 | import React from "react";
4 |
5 | const btnIcon = [
6 | "/assets/authNavBarControls/save.png",
7 | "/assets/authNavBarControls/save-1.png",
8 | "/assets/authNavBarControls/peers.png",
9 | "/assets/authNavBarControls/peers-1.png",
10 | "/assets/authNavBarControls/peers-2.png",
11 | ];
12 | function AuthNavControls() {
13 | return (
14 |
15 | {btnIcon.map((e, l) => {
16 | return (
17 |
20 | );
21 | })}
22 |
23 | );
24 | }
25 |
26 | export default AuthNavControls;
27 |
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/eye.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/sidebar_components/sideBarNavControl.js:
--------------------------------------------------------------------------------
1 | import SideBottomNavControl from "./sideBottomNavControl";
2 | import SideTopNavControl from "./sideTopNavControl";
3 |
4 | function SideNavBarControl({ handleTopNavClicks }) {
5 | return (
6 |
7 |
8 |
9 | {
11 | handleTopNavClicks(i);
12 | }}
13 | />
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | export default SideNavBarControl;
24 |
--------------------------------------------------------------------------------
/development/src/components/sessionManagerComp.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | function SessionManager() {
3 | return (
4 |
5 |
6 | Ready to start collaborating?
7 |
8 |
9 |
13 | New Peer Session
14 |
15 |
16 |
19 | Join Peer Session
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | export { SessionManager };
27 |
--------------------------------------------------------------------------------
/development/public/assets/githubGoogleError/closecircle.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/development/src/components/welcome_comp.js:
--------------------------------------------------------------------------------
1 | import Onboarding from "./onboarding_comp";
2 | import Image from "next/image";
3 | import welcome from "../../public/assets/dashboard/welcome_comp.svg";
4 | import { usePathname, useRouter, useSearchParams } from "next/navigation";
5 |
6 | const Welcome = () => {
7 | const router = useRouter();
8 | const view = useSearchParams().get("view");
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default Welcome;
30 |
--------------------------------------------------------------------------------
/development/server.js:
--------------------------------------------------------------------------------
1 | const http = require("http");
2 | const express = require("express");
3 | const cors = require("cors");
4 | const socketIO = require("socket.io");
5 | const app = express();
6 |
7 | const server = http.createServer(app);
8 | const io = socketIO(server, {
9 | cors: {
10 | origin: "*",
11 | methods: ["GET", "POST", "PUT", "DELETE"],
12 | allowedHeaders: ["Content-Type"],
13 | },
14 | });
15 |
16 | io.on("connection", (socket) => {
17 | socket.on("join-room", (room) => {
18 | socket.join(room);
19 | console.log(`Client joined room: ${room}`);
20 | });
21 |
22 | socket.on("text-update", (data) => {
23 | // Broadcast the received update to all connected clients
24 | io.to(data.room).emit("text-update", data.newValue);
25 | console.log(data);
26 | });
27 |
28 | socket.on("disconnect", () => {
29 | console.log("Client disconnected");
30 | });
31 | });
32 |
33 | server.listen(3001, () => {
34 | console.log("WebSocket server is running on port 3001");
35 | });
36 |
--------------------------------------------------------------------------------
/development/src/composables/firebaseConfig/config.js:
--------------------------------------------------------------------------------
1 | import { initializeApp } from "firebase/app";
2 | import { getFirestore } from "@firebase/firestore";
3 | import { getStorage } from "firebase/storage";
4 | import { GoogleAuthProvider, getAuth } from "firebase/auth";
5 |
6 | const firebaseConfig = {
7 | apiKey: process.env.NEXT_PUBLIC_API_KEY,
8 | authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
9 | projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
10 | storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
11 | messagingSenderId: process.env.NEXT_PUBLIC_SENDER_ID,
12 | appId: process.env.NEXT_PUBLIC_APP_ID,
13 | measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID,
14 | };
15 |
16 | // Init firebase
17 | const app = initializeApp(firebaseConfig);
18 |
19 | // init service
20 | const appFirestore = getFirestore(app);
21 | const appStorage = getStorage(app);
22 | const appAuth = getAuth(app);
23 | const emailProvider = new GoogleAuthProvider();
24 | // timestamp
25 | const timestamp = appFirestore.timestamp;
26 |
27 | export { appStorage, timestamp, appAuth, appFirestore, emailProvider };
28 |
--------------------------------------------------------------------------------
/development/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "p2pcoder",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "stage":"next dev",
7 | "dev": "concurrently \"next dev\" \"node server.js\"",
8 | "build": "next build",
9 | "start": "next start",
10 | "lint": "next lint",
11 | "api": "node ./pages/api/socket.js",
12 | "stream": "node server.js"
13 | },
14 | "dependencies": {
15 | "@headlessui/react": "^1.7.14",
16 | "autoprefixer": "10.4.14",
17 | "encoding": "^0.1.13",
18 | "eslint": "8.40.0",
19 | "eslint-config-next": "13.4.2",
20 | "express": "^4.18.2",
21 | "firebase": "^9.22.0",
22 | "monaco-editor-webpack-plugin": "^7.0.1",
23 | "next": "^13.4.5",
24 | "next-themes": "^0.2.1",
25 | "postcss": "8.4.23",
26 | "react": "18.2.0",
27 | "react-dom": "18.2.0",
28 | "react-monaco-editor": "^0.52.0",
29 | "socket.io": "^4.6.2",
30 | "socket.io-client": "^4.6.2",
31 | "tailwindcss": "3.3.2",
32 | "twilio": "^4.11.2",
33 | "utf-8-validate": "^5.0.10"
34 | },
35 | "devDependencies": {
36 | "concurrently": "^8.2.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/development/src/components/errorModal_comp.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import closeIcon from '../../public/assets/githubGoogleError/closecircle.svg';
4 | import Image from 'next/image';
5 | import errorIcons from '../../public/assets/githubGoogleError/error.svg';
6 |
7 | function ErrorModal({ onClose, errorMessage, style }) {
8 | if (!errorMessage) {
9 | return null;
10 | }
11 |
12 | return (
13 |
14 |
15 |
21 |
22 |
{errorMessage}
23 |
24 |
36 |
37 |
38 | );
39 | }
40 |
41 | export default ErrorModal;
42 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/tabbar_components/tabBarControls_comp.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import TabBarItems from './tabBarItem';
3 |
4 | function TabBarControls({
5 | items,
6 | handleActiveTab,
7 | handleCloseTab,
8 | handleRenameTab,
9 | }) {
10 | if (!Array.isArray(items)) {
11 | return null;
12 | }
13 |
14 | return (
15 |
16 |
17 |
18 | {items.map((e, l) => {
19 | return (
20 | {
26 | handleCloseTab(l, event);
27 | }}
28 | onClick={(event) => {
29 | handleActiveTab(l, event);
30 | }}
31 | onDoubleClick={(event) => {
32 | handleRenameTab(l, event);
33 | }}
34 | />
35 | );
36 | })}
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | export default TabBarControls;
44 |
--------------------------------------------------------------------------------
/development/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/development/src/app/localFont.js:
--------------------------------------------------------------------------------
1 | import localFont from "next/font/local";
2 | const nohemi = localFont({
3 | src: [
4 | {
5 | path: "./fonts/Nohemi-Black-BF6438cc565e67b.woff",
6 | weight: "900",
7 | style: "black",
8 | },
9 |
10 | {
11 | path: "./fonts/Nohemi-Bold-BF6438cc577b524.woff",
12 | weight: "700",
13 | style: "bold",
14 | },
15 | {
16 | path: "./fonts/Nohemi-ExtraBold-BF6438cc5761ae2.woff",
17 | weight: "800",
18 | style: "extrabold",
19 | },
20 | {
21 | path: "./fonts/Nohemi-ExtraLight-BF6438cc581502c.woff",
22 | weight: "200",
23 | style: "extralight",
24 | },
25 | {
26 | path: "./fonts/Nohemi-Light-BF6438cc5702321.woff",
27 | weight: "300",
28 | style: "light",
29 | },
30 | {
31 | path: "./fonts/Nohemi-Medium-BF6438cc57ddecd.woff",
32 | weight: "500",
33 | style: "medium",
34 | },
35 | {
36 | path: "./fonts/Nohemi-Regular-BF6438cc579d934.woff",
37 | weight: "400",
38 | style: "normal",
39 | },
40 | {
41 | path: "./fonts/Nohemi-SemiBold-BF6438cc57db2ff.woff",
42 | weight: "600",
43 | style: "black",
44 | },
45 | {
46 | path: "./fonts/Nohemi-Thin-BF6438cc57e2011.woff",
47 | weight: "100",
48 | style: "thin",
49 | },
50 | ],
51 | variable: "--font-nohemi",
52 | });
53 |
54 | export { nohemi };
55 |
--------------------------------------------------------------------------------
/development/public/assets/openTabIcons/users.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/closecirclelight.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/development/src/components/session_comp.js:
--------------------------------------------------------------------------------
1 | import { SessionManager } from "./sessionManagerComp";
2 | import { PeerSession } from "./PeerOverlay_comp";
3 | import { Modal } from "./modal";
4 | import { useRouter, useSearchParams } from "next/navigation";
5 | import JoinSession from "./joinSession_Comp";
6 | import PeerId from "./peerId_comp";
7 |
8 | function SessionComp() {
9 | const router = useRouter();
10 | const view = useSearchParams().get("view");
11 |
12 | return (
13 |
14 | {view == "createSession" ? (
15 |
{
17 | router.push("/");
18 | }}
19 | >
20 | {
22 | router.push("/");
23 | }}
24 | />
25 |
26 | ) : view == "joinSession" ? (
27 |
{
29 | router.push("/");
30 | }}
31 | >
32 | {
34 | router.push("/");
35 | }}
36 | />
37 |
38 | ) : view == "peerId" ? (
39 |
{
41 | router.push("/");
42 | }}
43 | >
44 | {
46 | router.push("/");
47 | }}
48 | />
49 |
50 | ) : (
51 |
52 | )}
53 |
54 |
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export default SessionComp;
62 |
--------------------------------------------------------------------------------
/development/src/components/VerificationSuccessful_comp.js:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import Image from "next/image";
3 | import closeIconBlack from "../../public/assets/onboardingIcons/close_black.png";
4 | import closeIconWhite from "../../public/assets/onboardingIcons/close_light.png";
5 | import { useTheme } from "next-themes";
6 | import { useState } from "react";
7 |
8 | function VerificationSuccessful() {
9 | const { theme, setTheme } = useTheme();
10 | const [showOverlay, setShowOverlay] = useState(true);
11 |
12 | const handleCloseForm = () => {
13 | setShowOverlay(false);
14 | router.push("/");
15 | };
16 |
17 | return (
18 |
19 |
20 |
21 |
22 | Verification Successful
23 |
24 |
25 |
31 |
32 |
33 |
34 | Account has been successfully verified with Carai
35 |
36 |
40 | Log in
41 |
42 |
43 |
44 | );
45 | }
46 | export default VerificationSuccessful;
47 |
--------------------------------------------------------------------------------
/development/public/assets/onboardingIcons/eye-slash.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/development/src/components/codingEditor.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import MonacoEditor from "react-monaco-editor/lib/editor";
3 | import { useTheme } from "next-themes";
4 | import { useTabContext } from "@/composables/tabContext";
5 | import io from "socket.io-client";
6 | import { useEffect, useState } from "react";
7 |
8 | function CodingEditor({ peerid }) {
9 | const { theme, setTheme } = useTheme();
10 | const { items } = useTabContext();
11 | const [codes, setCodes] = useState("");
12 | const language = items.filter((e) => e.active)[0].ext;
13 | const socket = io("http://localhost:3001");
14 |
15 | useEffect(() => {
16 | socket.emit("join-room", peerid);
17 | socket.on("text-update", (data) => {
18 | items.filter((e) => e.active)[0].code = data;
19 | setCodes(data);
20 | });
21 |
22 | return () => {
23 | socket.off("text-update");
24 | };
25 | }, [codes]);
26 |
27 | const editorDidMount = (editor, monaco) => {
28 | editor.focus();
29 | };
30 |
31 | const onChange = (newValue, e) => {
32 | items.filter((e) => e.active)[0].code = newValue;
33 | socket.emit("text-update", { peerid, newValue });
34 | setCodes(newValue);
35 | };
36 |
37 | const options = {
38 | selectOnLineNumbers: true,
39 | automaticLayout: true,
40 | };
41 | return (
42 |
43 | e.active)[0].code}
56 | options={options}
57 | onChange={onChange}
58 | editorDidMount={editorDidMount}
59 | />
60 |
61 | );
62 | }
63 |
64 | export default CodingEditor;
--------------------------------------------------------------------------------
/development/src/composables/authGoogleSigninPoppup.js:
--------------------------------------------------------------------------------
1 | import { signInWithPopup, GoogleAuthProvider } from "firebase/auth";
2 | import { appAuth } from "./firebaseConfig/config";
3 | import { useState } from "react";
4 |
5 | const provider = new GoogleAuthProvider();
6 |
7 | function useGoogleSignin() {
8 | const [error, setError] = useState("");
9 | const [isPending, setIsPending] = useState(false);
10 |
11 | async function signinWithGoogle() {
12 | setError(null);
13 | setIsPending(true);
14 | try {
15 | const res = await signInWithPopup(appAuth, provider);
16 | if (!res) {
17 | setError("Couldn't complete signup");
18 | }
19 |
20 | const user = res.user;
21 | setIsPending(false);
22 |
23 | return { success: true };
24 | } catch (error) {
25 | const errorCode = error.code;
26 | let errorMessage = error.message;
27 | const credential = GoogleAuthProvider.credentialFromError(error);
28 |
29 | switch (errorCode) {
30 | case "auth/account-exists-with-different-credential":
31 | errorMessage =
32 | "This Google account is already linked with another login method";
33 | break;
34 | case "auth/popup-blocked":
35 | errorMessage =
36 | "Sign-in popup blocked by your browser's settings. Please disable the popup blocker and try signing in again. Alternatively, you can try signing in using a different browser.";
37 | break;
38 | case "auth/cancelled-popup-request":
39 | errorMessage =
40 | "Sign-in process canceled. Please retry Google sign-in.";
41 | break;
42 | case "auth/popup-closed-by-user":
43 | errorMessage =
44 | "Sign-in popup closed. Please complete Google sign-in to proceed";
45 |
46 | break;
47 | default:
48 | errorMessage = "An error occurred. Please try again later.";
49 | }
50 |
51 | setError(errorMessage);
52 | setIsPending(false);
53 | }
54 | }
55 | return { signinWithGoogle, googleError: error, isPending };
56 | }
57 |
58 | export { useGoogleSignin };
59 |
--------------------------------------------------------------------------------
/development/src/composables/authGithubSigninPopup.js:
--------------------------------------------------------------------------------
1 | import { signInWithPopup, GithubAuthProvider } from "firebase/auth";
2 | import { appAuth } from "./firebaseConfig/config";
3 | import { useState } from "react";
4 |
5 | const provider = new GithubAuthProvider();
6 |
7 | function useGithubSignin() {
8 | const [error, setError] = useState("");
9 | const [isPending, setIsPending] = useState(false);
10 |
11 | async function signinWithGithub() {
12 | setError(null);
13 | setIsPending(true);
14 | try {
15 | const res = await signInWithPopup(appAuth, provider);
16 | if (!res) {
17 | setError("Couldn't complete signup");
18 | }
19 | const user = res.user;
20 | setIsPending(false);
21 | return { success: true };
22 | } catch (error) {
23 | const errorCode = error.code;
24 | let errorMessage = error.message;
25 | const credential = GithubAuthProvider.credentialFromError(error);
26 |
27 | switch (errorCode) {
28 | case "auth/account-exists-with-different-credential":
29 | errorMessage =
30 | "This GitHub account is already linked with another login method";
31 | break;
32 |
33 | case "auth/popup-blocked":
34 | errorMessage =
35 | "Sign-in popup blocked by your browser's settings. Please disable the popup blocker and try signing in again. Alternatively, you can try signing in using a different browser.";
36 | break;
37 | case "auth/cancelled-popup-request":
38 | errorMessage =
39 | "Sign-in process canceled. Please retry GitHub sign-in.";
40 | break;
41 | case "auth/popup-closed-by-user":
42 | errorMessage =
43 | "Sign-in popup closed. Please complete GitHub sign-in to proceed";
44 |
45 | break;
46 | default:
47 | errorMessage = "An error occurred. Please try again later.";
48 | }
49 |
50 | setError(errorMessage);
51 |
52 | setIsPending(false);
53 | }
54 | }
55 |
56 | return { signinWithGithub, githubError: error, isPending };
57 | }
58 |
59 | export { useGithubSignin };
60 |
--------------------------------------------------------------------------------
/development/src/composables/authSignupFunction.js:
--------------------------------------------------------------------------------
1 | import {
2 | createUserWithEmailAndPassword,
3 | updateProfile,
4 | sendEmailVerification,
5 | } from "firebase/auth";
6 | import { appAuth, appFirestore } from "./firebaseConfig/config";
7 | import { doc, setDoc } from "firebase/firestore";
8 |
9 | async function authSignUp(firstname, lastname, email, password, username) {
10 | try {
11 | const userCredential = await createUserWithEmailAndPassword(
12 | appAuth,
13 | email,
14 | password
15 | );
16 | const user = userCredential.user;
17 | if (user) {
18 | await updateProfile(user, { displayName: `${firstname} ${lastname}` });
19 |
20 | const newUser = {
21 | firstname,
22 | lastname,
23 | email,
24 | };
25 | const completeSignupResponse = await completeSignUp(
26 | newUser,
27 | user.uid,
28 | username
29 | );
30 | if (completeSignupResponse.success) {
31 | return { success: true, user };
32 | } else {
33 | return { success: false, error: completeSignupResponse.error };
34 | }
35 | }
36 | } catch (error) {
37 | return { success: false, error: error.message };
38 | }
39 | }
40 |
41 | async function triggerEmailVerification(user) {
42 | try {
43 | await sendEmailVerification(user);
44 | return { success: true };
45 | } catch (error) {
46 | return { success: false, error: error.message };
47 | }
48 | }
49 |
50 | async function completeSignUp(user, uid, username) {
51 | const newDocRef = doc(appFirestore, "CODERS", uid);
52 |
53 | const fullname = `${user.firstname} ${user.lastname}`;
54 |
55 | const dataToSet = {
56 | fullname,
57 | username,
58 | email: user.email,
59 | createdAt: new Date(),
60 | };
61 |
62 | try {
63 | await setDoc(newDocRef, dataToSet);
64 | console.log("Document successfully written.");
65 | return { success: true };
66 | } catch (error) {
67 | console.error("Error writing document: ", error);
68 | return { success: false, error: error.message };
69 | }
70 | }
71 |
72 | export { authSignUp, completeSignUp, triggerEmailVerification };
73 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/sidebar_components/sideBottomNavControl.js:
--------------------------------------------------------------------------------
1 | import { appAuth } from "@/composables/firebaseConfig/config";
2 | import { handleLogout } from "@/composables/signOutFunction";
3 | import { isUserSignedIn } from "@/composables/verifySignedIn";
4 | import Image from "next/image";
5 | import { useRouter } from "next/navigation";
6 | import { useEffect, useState } from "react";
7 |
8 | const btnNav = [
9 | "/assets/sideBottomNavControls/logout.png",
10 | "/assets/sideBottomNavControls/settings.png",
11 | ];
12 | function SideBottomNavControl() {
13 | const router = useRouter();
14 | const [auth, setAuth] = useState(false);
15 | const user = appAuth.currentUser;
16 | useEffect(() => {
17 | if (isUserSignedIn()) {
18 | setAuth(true);
19 | } else {
20 | setAuth(false);
21 | }
22 | }, [user]);
23 |
24 | const signUserOut = async () => {
25 | const response = await handleLogout();
26 | setAuth(isUserSignedIn());
27 | response.success ? router.push("/?view=login") : "";
28 | };
29 |
30 | return (
31 |
32 | {auth ? (
33 | btnNav.map((e, i) => {
34 | if (e.includes("logout")) {
35 | return (
36 |
48 | );
49 | }
50 | })
51 | ) : (
52 |
60 | )}
61 |
62 | );
63 | }
64 |
65 | export default SideBottomNavControl;
66 |
--------------------------------------------------------------------------------
/development/src/components/onboarding_comp.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import { useTheme } from "next-themes";
4 | import { isUserSignedIn } from "@/composables/verifySignedIn";
5 |
6 | function Onboarding() {
7 | const { theme, setTheme } = useTheme();
8 | return (
9 |
10 |
11 | Get Started With Carai
12 |
13 |
14 | -
15 |
16 | {" "}
17 | Share, Review and Improve your Code.
18 |
19 |
20 | -
21 | {" "}
22 |
Connect and code with peers.
23 |
24 | -
25 | {" "}
26 |
Communicate in real-time.
27 |
28 | -
29 | {" "}
30 |
Keep track of changes.
31 |
32 | -
33 | {" "}
34 |
Start collaborating today.
35 |
36 |
37 |
38 | {!isUserSignedIn() && (
39 | <>
40 |
44 | Create Account
45 |
46 |
50 | Log In
51 |
52 | >
53 | )}
54 |
55 |
56 | );
57 | }
58 |
59 | export default Onboarding;
60 |
--------------------------------------------------------------------------------
/development/src/composables/signupFormValidation.js:
--------------------------------------------------------------------------------
1 | function signupFormValidation(user) {
2 | const errors = {};
3 |
4 | const regxName = /^[A-Za-z\-]{2,50}$/;
5 | const trimmedFirstName = user.firstname.trim();
6 | if (!trimmedFirstName) {
7 | errors.firstname = "Please enter your first name";
8 | } else if (trimmedFirstName < 2 || trimmedFirstName > 50) {
9 | errors.firstname = "Name must be between 2 and 50 characters";
10 | } else if (!regxName.test(trimmedFirstName)) {
11 | errors.firstname = "Please enter a valid name";
12 | }
13 |
14 | const regxLastName = /^[A-Za-z\-]{2,50}$/;
15 | const trimmedLastName = user.lastname.trim();
16 | if (!trimmedLastName) {
17 | errors.lastname = "Please enter your last name";
18 | } else if (trimmedLastName < 2 || trimmedLastName > 50) {
19 | errors.lastname = "Name must be between 2 and 50 characters";
20 | } else if (!regxLastName.test(trimmedLastName)) {
21 | errors.lastname = "Please enter a valid name";
22 | }
23 |
24 | const regxEmail = /^([a-zA-Z0-9\.]+)@([a-zA-Z]+)\.([a-z]+)(\.[a-z]+)?$/;
25 | const trimmedEmail = user.email.trim();
26 | if (!trimmedEmail) {
27 | errors.email = "Please enter your email address";
28 | } else if (!regxEmail.test(trimmedEmail)) {
29 | errors.email = "Please enter a valid email address";
30 | }
31 |
32 | const regxUsername = /^[a-zA-Z0-9_]{3,20}$/;
33 | const trimmedUserName = user.username.trim();
34 | if (!trimmedUserName) {
35 | errors.username = "Please enter your username";
36 | } else if (!regxUsername.test(trimmedUserName)) {
37 | errors.username = "Please enter a valid username";
38 | }
39 |
40 | const regxPassword =
41 | /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])(?!.*\s).{8,15}$/;
42 | const trimmedPassword = user.password.trim();
43 | if (!trimmedPassword) {
44 | errors.password = "Please enter a password";
45 | } else if (trimmedPassword.length < 8 || trimmedPassword.length > 20) {
46 | errors.password = "Please enter a password between 8 and 15 characters";
47 | } else if (!regxPassword.test(trimmedPassword)) {
48 | errors.password =
49 | "Password must contain one digit, one lowercase letter, one uppercase letter, and one special character";
50 | }
51 |
52 | if (!user.confirm_password) {
53 | errors.confirm_password = "Please confirm your password";
54 | } else if (user.password !== user.confirm_password) {
55 | errors.confirm_password = "Passwords do not match";
56 | }
57 |
58 | return errors;
59 | }
60 | export { signupFormValidation };
61 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/tabbar_components/tabBarItem.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import Image from 'next/image';
3 | import css from '../../../../public/assets/openTabIcons/CSS3.png';
4 | import html from '../../../../public/assets/openTabIcons/HTML.png';
5 | import js from '../../../../public/assets/openTabIcons/Javascript.png';
6 | import users from '../../../../public/assets/sideTopNavBar/user.png';
7 |
8 | function TabBarItems({ title, ext, onClose, active, onClick, onDoubleClick }) {
9 | return (
10 |
18 | {ext && (
19 |
36 | )}
37 |
44 | {title}
45 |
57 | {ext}
58 |
59 |
60 |
79 |
80 | );
81 | }
82 |
83 | export default TabBarItems;
84 |
--------------------------------------------------------------------------------
/development/src/components/joinSession_Comp.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { addUserToExistingSession } from "@/composables/dbService";
3 | import { useState } from "react";
4 | import ErrorModal from "./errorModal_comp";
5 | import { useRouter } from "next/navigation";
6 | import Link from "next/link";
7 | import { useTabContext } from "@/composables/tabContext";
8 |
9 | function JoinSession() {
10 | const [sessionId, setSessionId] = useState("");
11 | const [error, setError] = useState("");
12 | const [isUserAdded, setIsUserAdded] = useState(false);
13 | const router = useRouter();
14 | const { handleLanguage } = useTabContext();
15 |
16 | useEffect(() => {
17 | if (error !== "") {
18 | setTimeout(() => {
19 | setError("");
20 | }, 6000);
21 | }
22 | }, [error]);
23 |
24 | const handleErrorClose = () => {
25 | setError("");
26 | };
27 |
28 | const handleJoinSession = async (e) => {
29 | e.preventDefault();
30 | setIsUserAdded(false);
31 |
32 | if (!sessionId) {
33 | setError("Session ID is required");
34 | } else {
35 | const result = await addUserToExistingSession(sessionId);
36 | if (!result.success) {
37 | setError(result.message);
38 | } else {
39 | setIsUserAdded(true);
40 | await handleLanguage(result.sessionLanguage, true);
41 | router.push("/");
42 | }
43 | }
44 | };
45 | return (
46 |
47 |
48 |
Join Peer Session
49 |
50 |
51 |
Session ID
52 |
setSessionId(e.target.value)}
58 | placeholder="Enter Session ID"
59 | className="py-3 mt-2 mb-4 px-4 h-12 w-full font-normal dark:bg-[#1E1E2A] text-sm rounded-lg"
60 | />
61 |
62 |
68 |
69 |
70 |
71 |
72 |
handleErrorClose()}
76 | />
77 |
78 | );
79 | }
80 |
81 | export default JoinSession;
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # P2PCODER PLATFORM
2 |
3 | A p2p code learning platform that connects developers to a single code editor over the internet. They can write and edit code, and communicate via audio and video streaming.
4 |
5 | The goal of the project is to utilize Nextjs and technologies to build this project by newbies. The project has a mid-level degree of complexity, so would be broken down into difficulty levels throughout the 8 weeks.
6 |
7 | This document is to help developers understand the platform and the development requirements of the project.
8 | The document will be updated from time to time to meet project expectations and standards.
9 |
10 | # DEVELOPMENT
11 |
12 | During the development of the project, branches will be created for specific features/components/function development.
13 | PR will be created against this issue and will be merged to the `development` branch of the repository.
14 |
15 | To find new issues please visit [Projects](https://github.com/annonymousauthority/Carai/projects?query=is%3Aopen) or visit the [Issue](https://github.com/annonymousauthority/Carai/issues) tab.
16 | A developer can request for clarification of an issue before going on so to ensure full compliance with development expectations and requirements.
17 |
18 | ### FOLDERS AND NAMING STYLE
19 |
20 | The folder structure is as follows
21 |
22 | - public
23 | - public/assets : `For images used in the project`
24 | - src/app `For route pages`
25 | - src/components `for app components`
26 | - src/composables `for app functions`
27 |
28 | Folder naming would be responsibility of Team Lead. [Augustine](https://github.com/annonymousauthority)
29 |
30 | ### FUNCTIONS AND EXPORT STYLE
31 |
32 | The naming convention for components files would be
33 | `[name]_comp.js` eg. addButton_comp.js, highlightTree_comp.js
34 |
35 | We'll be using the single file export system for our components and page exports
36 |
37 | ### RUNNING SERVER
38 |
39 | ```bash
40 | npm run dev
41 | # or
42 | yarn dev
43 | # or
44 | pnpm dev
45 | ```
46 |
47 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
48 |
49 | ### PR GUIDELINES
50 |
51 | A branch can only be merged by owner [Augustine](https://github.com/annonymousauthority).
52 | But can be approved by anyone.
53 |
54 | ### TEAM
55 |
56 | - [Augustine](https://github.com/annonymousauthority) - Team Lead
57 | - [Oladipo](https://github.com/OladipoOmotosho) - Frontend Developer
58 | - [Remi](https://github.com/Remi-dee) - Frontend Developer
59 | - [Malik](https://github.com/7malikk) - Frontend Developer
60 | - [Taylor J](https://github.com/TaylorDJones11) - Software Engineer
61 | - [Ibraheem](https://github.com/Ibhassan01) - Frontend Developer
62 | - [Oyinbrakemi](https://github.com/Oyinbrakemigrace) - Frontend Developer
63 | - [Chidera](https://github.com/zer0szn) - web developer
64 | - [Deborah](https://github.com/DeborahIhesiaba) - Software Engineer
65 |
--------------------------------------------------------------------------------
/development/src/composables/tabContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useContext, useEffect, useState } from 'react';
2 |
3 | export const TabContext = createContext();
4 |
5 | export const TabProvider = ({ children }) => {
6 | const barItems = [{ id: 1, title: 'Welcome', active: true }];
7 | const [items, setItems] = useState([]);
8 | const [errorMessage, setErrorMessage] = useState('');
9 | const [isVideoOn, setIsVideoOn] = useState(false);
10 |
11 | useEffect(() => {
12 | if (items.length == 0) {
13 | setItems(barItems);
14 | } else {
15 | window.localStorage.setItem('barItems', JSON.stringify(items));
16 | }
17 | }, [items]);
18 | useEffect(() => {
19 | const storedItems = window.localStorage.getItem('barItems');
20 | if (storedItems) {
21 | setItems(JSON.parse(storedItems));
22 | } else {
23 | setItems(barItems);
24 | }
25 | }, []);
26 |
27 | const handleLanguage = (lang, isCollabOn) => {
28 | if (isCollabOn) {
29 | setIsVideoOn(true)
30 | }
31 | const oldItems = items.map((item) => ({
32 | ...item,
33 | active: false,
34 | }));
35 | if (lang && items.length < 5) {
36 | if (lang == 'collab') {
37 | const collabCheck = items.filter((e) => e.title == 'collab');
38 | if (!collabCheck.length) {
39 | const newItems = [
40 | ...oldItems,
41 | {
42 | id: items.length + 1,
43 | title: 'collab',
44 | ext: '.p2p',
45 | active: true,
46 | },
47 | ];
48 | setItems(newItems);
49 | } else {
50 | setErrorMessage('You can only have 1 collab tab');
51 | }
52 | } else {
53 | const newItems = [
54 | ...oldItems,
55 | {
56 | id: items.length + 1,
57 | title:
58 | lang === 'js'
59 | ? 'scripts'
60 | : lang === 'css'
61 | ? 'styles'
62 | : lang === 'html'
63 | ? 'index'
64 | : 'untitled',
65 | ext:
66 | lang === 'js'
67 | ? '.js'
68 | : lang === 'css'
69 | ? '.css'
70 | : lang === 'html'
71 | ? '.html'
72 | : '',
73 | active: true,
74 | code: '',
75 | isCollab: isVideoOn || isCollabOn
76 | },
77 | ];
78 | setItems(newItems);
79 | }
80 | } else {
81 | setErrorMessage('You can only have 5 pages open at once.');
82 | }
83 | };
84 |
85 | return (
86 |
97 | {children}
98 |
99 | );
100 | };
101 |
102 | export const useTabContext = () => {
103 | return useContext(TabContext);
104 | };
105 |
--------------------------------------------------------------------------------
/development/src/components/navbar_components/editorNavbar_comp.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { useState, useContext, useEffect } from 'react';
3 | import AuthNavControls from './authNavControls_comp';
4 | import Image from 'next/image';
5 | import moon from '../../../public/assets/onboardingIcons/moon.png';
6 | import sun from '../../../public/assets/onboardingIcons/sun.png';
7 | import { useTheme } from 'next-themes';
8 | import { useTabContext } from '@/composables/tabContext';
9 | import { Suspense } from 'react';
10 | import userIcon from '../../../public//assets/authNavBarControls/peers-2.png';
11 | import { isUserSignedIn } from '@/composables/verifySignedIn';
12 | import { appAuth } from '@/composables/firebaseConfig/config';
13 | import Link from 'next/link';
14 |
15 | function EditorNavBar() {
16 | const [auth, setAuth] = useState(true);
17 | const { theme, setTheme } = useTheme();
18 | const { items } = useTabContext();
19 |
20 | useEffect(() => {
21 | if (isUserSignedIn()) {
22 | setAuth(true);
23 | } else {
24 | setAuth(false);
25 | }
26 | }, [appAuth]);
27 |
28 | return (
29 |
30 |
31 |
32 |
33 | Carai
34 |
35 | {auth ? (
36 |
37 |
46 |
54 |
55 | ) : (
56 |
57 |
66 | {!isUserSignedIn() && (items.length > 1 || items[0]?.title !== "Welcome") && (
67 | <>
68 |
72 | Sign Up
73 |
74 |
78 | Log In
79 |
80 | >
81 | )}
82 |
83 | )}
84 |
85 |
86 |
87 | );
88 | }
89 |
90 | export default EditorNavBar;
91 |
--------------------------------------------------------------------------------
/development/src/components/VerificationOverlay.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import closeIcon from "../../public/assets/onboardingIcons/close_black.png";
4 | import closeIconWhite from "../../public/assets/onboardingIcons/close_light.png";
5 | import { triggerEmailVerification } from "@/composables/authSignupFunction";
6 | import { useTheme } from "next-themes";
7 | import { getAuth } from "firebase/auth";
8 | import { useState } from "react";
9 | import VerificationSuccessful from "./VerificationSuccessful_comp";
10 | import ChangeEmailOverlay from "./changeEmailOverlay_comp";
11 |
12 | const VerificationOverlay = ({ email, onClose }) => {
13 | const { theme, setTheme } = useTheme();
14 | const [verificationSuccess, setVerificationSuccess] = useState(false);
15 | const [isChangeEmailVisible, setChangeEmailVisible] = useState(false);
16 | const [errors, setErrors] = useState("");
17 |
18 | const handleVerifyEmail = async () => {
19 | const auth = getAuth();
20 | const user = auth.currentUser;
21 |
22 | if (user) {
23 | const response = await triggerEmailVerification(user);
24 | if (response.success) {
25 | const verificationCheckInterval = setInterval(async () => {
26 | await user.reload();
27 |
28 | if (user.emailVerified) {
29 | clearInterval(verificationCheckInterval);
30 | setVerificationSuccess(true);
31 | }
32 | }, 7000);
33 | } else {
34 | setErrors(response.error);
35 | }
36 | } else {
37 | setErrors("No user is signed in");
38 | }
39 | };
40 |
41 | const handleEmailChange = () => {
42 | setChangeEmailVisible(!isChangeEmailVisible);
43 | };
44 |
45 | return (
46 |
47 | {isChangeEmailVisible ? (
48 |
49 | ) : verificationSuccess ? (
50 |
51 | ) : (
52 |
53 |
54 |
55 | Account Verification
56 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | A verification link has been sent to
73 | {email} . Please check your
74 | inbox and click on the link to complete your registration.
75 |
76 |
77 |
78 | Change Email
79 |
80 |
89 |
90 | Did not receive the mail? Check your spam
91 |
92 |
93 | )}
94 |
95 | );
96 | };
97 | export default VerificationOverlay;
98 |
--------------------------------------------------------------------------------
/development/src/components/openTabModal_comp.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React, { useState } from "react";
3 | import HTML from "../../public/assets/openTabIcons/HTML.png";
4 | import CSS from "../../public/assets/openTabIcons/CSS3.png";
5 | import JS from "../../public/assets/openTabIcons/Javascript.png";
6 | import Collab from "../../public/assets/openTabIcons/users.svg";
7 | import { useTabContext } from "@/composables/tabContext";
8 | import { isUserSignedIn } from "@/composables/verifySignedIn";
9 | import { appAuth } from "@/composables/firebaseConfig/config";
10 |
11 | export const OpenTabModal = ({ onClose }) => {
12 | const { handleLanguage } = useTabContext();
13 | const [active, setActive] = useState("");
14 |
15 | const user = appAuth.currentUser;
16 |
17 | const handleSubmit = (e) => {
18 | e.preventDefault();
19 | if (active) {
20 | handleLanguage(active);
21 | setActive("");
22 | onClose();
23 | }
24 | };
25 | return (
26 |
27 |
28 |
Open New Tab
29 |
30 |
setActive("html")}
32 | className={` w-32 h-32 flex justify-center items-center flex-col rounded-md hover:cursor-pointer ${
33 | active === "html"
34 | ? "bg-blue-500 text-white"
35 | : " bg-gray-200 dark:bg-[#3D3D48]"
36 | }`}
37 | >
38 |
39 |
HTML
40 |
41 |
setActive("css")}
43 | className={` w-32 h-32 flex justify-center items-center flex-col rounded-md hover:cursor-pointer ${
44 | active === "css"
45 | ? "bg-blue-500 text-white "
46 | : " bg-gray-200 dark:bg-[#3D3D48]"
47 | }`}
48 | >
49 |
50 |
CSS
51 |
52 |
setActive("js")}
54 | className={` w-32 h-32 flex justify-center items-center flex-col rounded-md hover:cursor-pointer ${
55 | active === "js"
56 | ? "bg-blue-500 text-white"
57 | : " bg-gray-200 dark:bg-[#3D3D48]"
58 | }`}
59 | >
60 |
61 |
JS
62 |
63 | {isUserSignedIn() && (
64 |
setActive("collab")}
66 | className={` w-32 h-32 flex justify-center items-center flex-col rounded-md hover:cursor-pointer ${
67 | active === "collab"
68 | ? "bg-blue-500 text-white"
69 | : " bg-gray-200 dark:bg-[#3D3D48]"
70 | }`}
71 | >
72 |
80 |
Collaborate
81 |
82 | )}
83 |
84 |
90 |
91 |
92 | );
93 | };
94 |
--------------------------------------------------------------------------------
/development/src/components/ForgotPassword.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React, { useState } from "react";
3 | import { emailValidator } from "@/composables/emailPasswordValidator";
4 | import { resetPassword } from "@/composables/sendPasswordResetFunction";
5 | import closeWhite from ".././../public/assets/forgotPasswordForm/close_white.png";
6 | import closeBlack from ".././../public/assets/onboardingIcons/close_black.png";
7 | import { useTheme } from "next-themes";
8 | import { useRouter } from "next/navigation";
9 |
10 | const ForgotPassword = () => {
11 | const { theme, setTheme } = useTheme();
12 | const [emailAddress, setEmailAddress] = useState("");
13 | const [error, setError] = useState(false);
14 | const [success, setSuccess] = useState(false);
15 | const [errorMessage, setErrorMessage] = useState("");
16 | const [successMessage, setSuccessMessage] = useState("");
17 |
18 | async function resetPasswordClick(e) {
19 | e.preventDefault();
20 | if (emailAddress === "") {
21 | setError(true);
22 | setSuccess(false);
23 | setErrorMessage("Please enter an email address");
24 | } else if (!emailValidator(emailAddress)) {
25 | setError(true);
26 | setSuccess(false);
27 | setErrorMessage("Please enter a valid email address");
28 | } else {
29 | try {
30 | await resetPassword(emailAddress);
31 | setSuccess(true);
32 | setError(false);
33 | setSuccessMessage("Recovery mail sent");
34 | } catch (error) {
35 | setError(true);
36 | setSuccess(false);
37 | setErrorMessage("Password reset failed", error);
38 | }
39 | }
40 | }
41 |
42 | const router = useRouter();
43 |
44 | const handleCloseForgotPassword = () => {
45 | router.push("/");
46 | };
47 |
48 | return (
49 |
50 |
51 |
52 |
53 | Forgot Password
54 |
55 |
63 |
64 |
65 | Enter your email address to reset your password
66 |
67 |
108 |
109 |
110 | );
111 | };
112 |
113 | export { ForgotPassword };
114 |
--------------------------------------------------------------------------------
/development/src/components/changeEmailOverlay_comp.js:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import closeIcon from "../../public/assets/onboardingIcons/close_black.png";
4 | import closeIconWhite from "../../public/assets/onboardingIcons/close_light.png";
5 | import { triggerEmailVerification } from "@/composables/authSignupFunction";
6 | import VerificationSuccessful from "./VerificationSuccessful_comp";
7 | import { useTheme } from "next-themes";
8 | import { getAuth, updateEmail } from "firebase/auth";
9 | import { useState } from "react";
10 | import { appFirestore } from "@/composables/firebaseConfig/config";
11 | import { doc, setDoc } from "firebase/firestore";
12 |
13 | const ChangeEmailOverlay = ({ onClose }) => {
14 | const { theme, setTheme } = useTheme();
15 | const [newEmail, setNewEmail] = useState("");
16 | const [updateVerificationSuccess, setUpdateVerificationSuccess] =
17 | useState(false);
18 | const [errorMessage, setErrorMessage] = useState("");
19 |
20 | const handleUpdateVerifyEmail = async () => {
21 | const auth = getAuth();
22 | const user = auth.currentUser;
23 |
24 | if (user) {
25 | try {
26 | await updateEmail(user, newEmail);
27 |
28 | const response = await triggerEmailVerification(user);
29 | if (response.success) {
30 | const verificationCheckInterval = setInterval(async () => {
31 | await user.reload();
32 |
33 | if (user.emailVerified) {
34 | clearInterval(verificationCheckInterval);
35 | setUpdateVerificationSuccess(true);
36 | const newDocRef = doc(appFirestore, "CODERS", user.uid);
37 | await setDoc(newDocRef, { email: newEmail }, { merge: true });
38 | }
39 | }, 7000);
40 | } else {
41 | setErrorMessage({ firebaseError: response.error });
42 | }
43 | } catch (error) {
44 | if (error.code === "auth/email-already-in-use") {
45 | setErrorMessage({
46 | firebaseError:
47 | "The new email is already in use by another account.",
48 | });
49 | } else {
50 | setErrorMessage({
51 | firebaseError: "Error updating email: " + error.message,
52 | });
53 | }
54 | }
55 | } else {
56 | setErrorMessage("No user is signed in");
57 | }
58 | };
59 |
60 | return updateVerificationSuccess ? (
61 |
62 | ) : (
63 |
64 |
65 |
66 |
67 | Change Email Address
68 |
69 |
70 |
75 |
76 |
77 |
78 |
79 |
80 |
Re-enter email address
81 |
82 |
83 |
84 |
85 | setNewEmail(e.target.value)}
93 | value={newEmail}
94 | />
95 | {errorMessage && (
96 |
97 | {errorMessage.firebaseError}
98 |
99 | )}
100 |
101 |
102 |
103 |
112 |
113 |
114 | );
115 | };
116 | export default ChangeEmailOverlay;
117 |
--------------------------------------------------------------------------------
/development/src/components/passwordToggleFunction.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect, useState } from "react";
3 |
4 | const PasswordToggle = ({
5 | inputId,
6 | inputValue,
7 | handleInputChange,
8 | placeholder,
9 | customClass,
10 | }) => {
11 | const [isPasswordVisible, setIsPasswordVisible] = useState(false);
12 | const [isDefaultPassword, setIsDefaultPassword] = useState(true);
13 | const [eyeIcon, setEyeIcon] = useState(null);
14 |
15 | const handleToggleClick = () => {
16 | if (inputValue !== "") {
17 | setIsPasswordVisible(!isPasswordVisible);
18 | setIsDefaultPassword(false);
19 | } else if (isPasswordVisible) {
20 | setIsPasswordVisible(false);
21 | setIsDefaultPassword(true);
22 | }
23 | };
24 |
25 | useEffect(() => {
26 | setEyeIcon(() => {
27 | if (isPasswordVisible) {
28 | return (
29 |
57 | );
58 | } else {
59 | return (
60 |
76 | );
77 | }
78 | });
79 | }, [isPasswordVisible]);
80 |
81 | const passwordInputType = isDefaultPassword
82 | ? "password"
83 | : isPasswordVisible
84 | ? "text"
85 | : "password";
86 |
87 | return (
88 |
89 |
98 |
99 |
106 |
107 |
108 | );
109 | };
110 |
111 | export { PasswordToggle };
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/development/src/components/peerId_comp.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState, useEffect } from "react";
3 | import Image from "next/image";
4 | import { generatePeerIdCharacter } from "../../src/composables/peerIdGenerator";
5 | import { useTheme } from "next-themes";
6 | import closeIconBlack from "../../public/assets/onboardingIcons/close_black.png";
7 | import closeIconWhite from "../../public/assets/onboardingIcons/close_light.png";
8 | import { useSessionContext } from "@/composables/sessionContext";
9 | import { useTabContext } from "@/composables/tabContext";
10 | import { useRouter } from "next/navigation";
11 | import { createSession } from "@/composables/dbService";
12 | import { appAuth } from "@/composables/firebaseConfig/config";
13 | import ErrorModal from "./errorModal_comp";
14 |
15 | function PeerId({ onClose }) {
16 | const { theme, setTheme } = useTheme();
17 | const { sessionData, setSessionData } = useSessionContext();
18 |
19 | const [copyMessage, setCopyMessage] = useState("");
20 | const [peerSessionId, setPeerSessionId] = useState("");
21 | const { handleLanguage } = useTabContext();
22 | const [error, setError] = useState("");
23 |
24 | const router = useRouter();
25 |
26 | useEffect(() => {
27 | setPeerSessionId(generatePeerIdCharacter());
28 | }, []);
29 |
30 | useEffect(() => {
31 | if (error !== "") {
32 | setTimeout(() => {
33 | setError("");
34 | }, 6000);
35 | }
36 | }, [error]);
37 |
38 | const handleErrorClose = () => {
39 | setError("");
40 | };
41 |
42 | const handleClick = async (e) => {
43 | e.preventDefault();
44 | await setSessionData({ ...sessionData, peerSessionId });
45 | const result = await createSession({ ...sessionData, peerSessionId });
46 | console.log(result.success);
47 | if (!result.success) {
48 | setError(result.message);
49 | console.log(error);
50 | } else {
51 | await handleLanguage(sessionData.activeLanguage, true);
52 | router.push("/");
53 | }
54 | };
55 |
56 | const handleClose = () => {
57 | setSessionData({});
58 | onClose();
59 | };
60 |
61 | const handleCopyId = async () => {
62 | try {
63 | await navigator.clipboard.writeText(peerSessionId);
64 | setCopyMessage("ID copied to clipboard:", peerSessionId);
65 | } catch (error) {
66 | setCopyMessage("Failed to copy ID to clipboard:", error);
67 | }
68 | };
69 |
70 | return (
71 |
72 |
73 |
74 | Peer Session Created
75 |
76 |
82 |
83 |
84 |
85 |
86 | PeerSession ID
87 |
88 |
89 | {peerSessionId}
90 |
91 |
92 |
119 |
120 |
121 | {copyMessage}
122 |
123 |
129 |
130 |
handleErrorClose()}
134 | />
135 |
136 | );
137 | }
138 |
139 | export default PeerId;
140 |
--------------------------------------------------------------------------------
/development/src/components/PeerOverlay_comp.js:
--------------------------------------------------------------------------------
1 | import { useTheme } from "next-themes";
2 | import Image from "next/image";
3 | import HTML from "../../public/assets/codeEditorIcons/symbol.png";
4 | import CSS from "../../public/assets/codeEditorIcons/CSS3.png";
5 | import JS from "../../public/assets/codeEditorIcons/Group.png";
6 | import closeIconWhite from "../../public/assets/onboardingIcons/close_light.png";
7 | import closeIconBlack from "../../public/assets/onboardingIcons/close_black.png";
8 | import React, { useEffect, useState } from "react";
9 |
10 | import { useRouter } from "next/navigation";
11 | import ErrorModal from "./errorModal_comp";
12 | import { useSessionContext } from "@/composables/sessionContext";
13 |
14 | function PeerSession({ onClose }) {
15 | const { setSessionData } = useSessionContext();
16 | const [activeLanguage, setActiveLanguage] = useState("");
17 | const [sessionName, setSessionName] = useState("");
18 | const route = useRouter();
19 | const [error, setError] = useState("");
20 |
21 | useEffect(() => {
22 | if (error !== "") {
23 | setTimeout(() => {
24 | setError("");
25 | }, 6000);
26 | }
27 | }, [error]);
28 |
29 | function createPeerSession() {
30 | if (!sessionName || !activeLanguage) {
31 | setError("Session name and Programming language are required");
32 | } else {
33 | route.push("/?view=peerId");
34 | setSessionData({ sessionName, activeLanguage });
35 | }
36 | }
37 |
38 | const handleErrorClose = () => {
39 | setError("");
40 | };
41 |
42 | const handleClose = () => {
43 | onClose();
44 | };
45 |
46 | const { theme, setTheme } = useTheme();
47 |
48 | return (
49 |
50 |
51 |
52 |
53 | New Peer Session
54 |
55 |
62 |
63 |
64 |
65 | Session Name
66 |
67 |
setSessionName(e.target.value)}
73 | placeholder="Enter Session Name"
74 | className="py-3 mt-2 mb-4 px-4 h-12 w-full font-nohemi dark:bg-[#1E1E2A] bg-gray-200 font-normal text-sm rounded-lg"
75 | />
76 |
77 |
78 |
79 | Select Language
80 |
81 |
setActiveLanguage(e.target.activeLanguage)}
84 | >
85 |
setActiveLanguage("html")}
87 | className={` w-32 h-32 flex justify-center items-center flex-col shadow-md rounded-md cursor-pointer ${
88 | activeLanguage === "html"
89 | ? "bg-blue-500 text-white"
90 | : " bg-gray-200 dark:bg-[#3D3D48]"
91 | }`}
92 | >
93 |
94 |
HTML
95 |
96 |
setActiveLanguage("css")}
98 | className={` w-32 h-32 flex justify-center items-center shadow-md flex-col rounded-md cursor-pointer ${
99 | activeLanguage === "css"
100 | ? "bg-blue-500 text-white "
101 | : " bg-gray-200 dark:bg-[#3D3D48]"
102 | }`}
103 | >
104 |
105 |
CSS
106 |
107 |
setActiveLanguage("js")}
109 | className={` w-32 h-32 flex justify-center shadow-md items-center flex-col rounded-md cursor-pointer ${
110 | activeLanguage === "js"
111 | ? "bg-blue-500 text-white"
112 | : " bg-gray-200 dark:bg-[#3D3D48]"
113 | }`}
114 | >
115 |
116 |
JS
117 |
118 |
119 |
120 |
121 |
127 |
128 |
129 |
handleErrorClose()}
133 | />
134 |
135 | );
136 | }
137 |
138 | export { PeerSession };
139 |
--------------------------------------------------------------------------------
/development/src/components/collab_comp.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CodingEditor from "./codingEditor";
3 | import { useSessionContext } from "@/composables/sessionContext";
4 | import { useEffect } from "react";
5 | import WebCamRecorder from "./webcam_comp";
6 | import { useStoreSession } from "@/composables/dbService";
7 |
8 | const Collab = ({isCollabOn}) => {
9 | const { sessionData } = useSessionContext();
10 | const { storeSession, getStoreSessionDetails } = useStoreSession();
11 |
12 | useEffect(() => {
13 | getStoreSessionDetails(sessionData.peerSessionId);
14 | }, [sessionData]);
15 |
16 | return (
17 |
18 |
19 |
20 |
21 | {isCollabOn && (
22 |
23 |
24 |
Peer Session ID
25 |
26 |
27 | {storeSession.peerId}
28 |
29 |
43 |
44 |
45 |
46 | {}}
48 | peername={storeSession.collaboratorName}
49 | isUser={false}
50 | />
51 | {}}
53 | peername={storeSession.codersName}
54 | isUser={true}
55 | />
56 |
57 |
58 |
84 |
85 |
86 | )}
87 |
88 | );
89 | };
90 |
91 | export default Collab;
92 |
--------------------------------------------------------------------------------
/development/src/composables/dbService.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 |
3 | import { appFirestore, appAuth } from "./firebaseConfig/config";
4 | import { setDoc, doc, updateDoc, getDoc, onSnapshot } from "firebase/firestore";
5 |
6 | async function createSession(userSessionData) {
7 | const { sessionName, activeLanguage, peerSessionId } = userSessionData;
8 | if (!sessionName || !activeLanguage || !peerSessionId) {
9 | throw new Error("Kindly provide all fields");
10 | }
11 |
12 | const user = appAuth.currentUser;
13 |
14 | if (user == null) {
15 | throw new Error("User not found!");
16 | }
17 |
18 | const coders = doc(appFirestore, `CODERS/${peerSessionId}`);
19 | const session = doc(coders, `SESSION/${user.uid}`);
20 |
21 | try {
22 | const sessionData = {
23 | sessionName: sessionName,
24 | peerId: peerSessionId,
25 | language: activeLanguage,
26 | codersName: user.username || user.displayName,
27 | createdAt: new Date(),
28 | };
29 |
30 | const userDocRef = await setDoc(
31 | coders,
32 | { sessionId: user.uid },
33 | { merge: true }
34 | );
35 | await setDoc(session, sessionData, { merge: true });
36 | return {
37 | sessionLanguage: activeLanguage,
38 | success: true,
39 | message: "Session successfully created!",
40 | };
41 | } catch (error) {
42 | return { success: false, message: error.message };
43 | }
44 | }
45 |
46 | function useStoreSession() {
47 | const [storeSession, setStoreSession] = useState({});
48 |
49 | async function getStoreSessionDetails(peerId) {
50 | const user = appAuth.currentUser;
51 | if (user == null) {
52 | throw new Error("User not found!");
53 | }
54 |
55 | const coders = doc(appFirestore, `CODERS/${peerId}`);
56 |
57 | const codersSnap = await getDoc(coders);
58 | if (!codersSnap.exists()) {
59 | throw new Error(`Session with ID ${peerId} does not exist`);
60 | }
61 |
62 | try {
63 | const sessionId = codersSnap.data().sessionId;
64 |
65 | const userSession = doc(coders, `SESSION/${sessionId}`);
66 |
67 | const sessionData = await getDoc(userSession);
68 | onSnapshot(userSession, (querySnapShot) => {
69 | const docData = querySnapShot.data();
70 | setStoreSession(docData);
71 | });
72 | } catch (error) {
73 | console.error("Error retrieving document: ", error);
74 | }
75 | }
76 |
77 | return { storeSession, getStoreSessionDetails };
78 | }
79 |
80 | async function addCollabCodeEditor(codeEditorData) {
81 | const { editorCode, peerId } = codeEditorData;
82 | if (!editorCode || !peerId) {
83 | throw new Error("Kindly insert text");
84 | }
85 | const user = appAuth.currentUser;
86 | if (user == null) {
87 | throw new Error("User not found!");
88 | }
89 |
90 | const coders = doc(appFirestore, `CODERS/${user.uid}`);
91 | const session = doc(coders, `SESSION/${peerId}`);
92 | try {
93 | const codeData = {
94 | codes: editorCode,
95 | };
96 |
97 | const docRef = await setDoc(session, codeData, { merge: true });
98 | return "code editor data added";
99 | } catch (error) {
100 | console.error("Error adding document: ", error);
101 | }
102 | }
103 |
104 | async function updateSession(updatedSessionData) {
105 | const { editorCode, peerSessionId, activeLanguage } = updatedSessionData;
106 | if (!editorCode || !peerSessionId || !activeLanguage) {
107 | throw new Error("Kindly provide all fields");
108 | }
109 |
110 | const user = appAuth.currentUser;
111 | if (user == null) {
112 | throw new Error("User not found!");
113 | }
114 |
115 | const coders = doc(appFirestore, `CODERS/${user.uid}`);
116 | const session = doc(coders, `SESSION/${peerSessionId}`);
117 |
118 | try {
119 | const sessionData = {
120 | codes: editorCode,
121 | sessionName: sessionName,
122 | peerId: peerSessionId,
123 | language: language,
124 | endedAt: new Date(),
125 | };
126 | const docRef = await updateDoc(session, sessionData);
127 | return "session updated!";
128 | } catch (error) {
129 | console.error("Error adding document: ", error);
130 | }
131 | }
132 |
133 | async function addUserToExistingSession(peerId) {
134 | try {
135 | if (!peerId) {
136 | throw new Error("User or session ID is missing");
137 | }
138 |
139 | const currentUser = appAuth.currentUser;
140 |
141 | if (!currentUser) {
142 | throw new Error("User is not authenicated");
143 | }
144 | const uid = currentUser.uid;
145 |
146 | const coders = doc(appFirestore, "CODERS", peerId);
147 | const codersSnap = await getDoc(coders);
148 | if (!codersSnap.exists()) {
149 | throw new Error(`Session with ID ${peerId} does not exist`);
150 | }
151 |
152 | const sessionId = codersSnap.data().sessionId;
153 |
154 | const sessionRef = doc(
155 | appFirestore,
156 | "CODERS",
157 | peerId,
158 | "SESSION",
159 | sessionId
160 | );
161 |
162 | const sessionRefSnap = await getDoc(sessionRef);
163 | const activeLanguage = sessionRefSnap.data().language;
164 |
165 | const collabUserData = {
166 | collaboratorName: currentUser.displayName || currentUser.username,
167 | };
168 | const sesDocRef = await setDoc(sessionRef, collabUserData, { merge: true });
169 |
170 | return {
171 | sessionLanguage: activeLanguage,
172 | success: true,
173 | message: "User successfully added to session",
174 | };
175 | } catch (err) {
176 | return { success: false, message: err.message };
177 | }
178 | }
179 |
180 | export {
181 | addCollabCodeEditor,
182 | createSession,
183 | updateSession,
184 | addUserToExistingSession,
185 | useStoreSession,
186 | };
187 |
--------------------------------------------------------------------------------
/development/src/app/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Welcome from "@/components/welcome_comp";
3 | import EditorNavBar from "@/components/navbar_components/editorNavbar_comp";
4 | import SideNavBarControl from "@/components/navbar_components/sidebar_components/sideBarNavControl";
5 | import TabBarControls from "@/components/navbar_components/tabbar_components/tabBarControls_comp";
6 | import { useSearchParams, useRouter } from "next/navigation";
7 | import { Modal } from "@/components/modal";
8 | import { OpenTabModal } from "@/components/openTabModal_comp";
9 | import { useTabContext } from "@/composables/tabContext";
10 | import Collab from "@/components/collab_comp";
11 | import { useEffect } from "react";
12 | import ErrorModal from "@/components/errorModal_comp";
13 | import UserLoginComp from "@/components/userLogin_comp";
14 | import SignUpComponent from "@/components/signup_comp";
15 | import SessionComp from "@/components/session_comp";
16 | import { ForgotPassword } from "@/components/ForgotPassword";
17 | import VerificationOverlay from "@/components/VerificationOverlay";
18 | import VerificationSuccessful from "@/components/VerificationSuccessful_comp";
19 |
20 | function Home() {
21 | const { items, setItems, errorMessage, setErrorMessage } = useTabContext();
22 | const view = useSearchParams().get("view");
23 | const router = useRouter();
24 |
25 | const handleTabActive = (tab) => {
26 | const index = items.findIndex((i, k) => k === tab);
27 | const newItems = items.map((item, idx) => ({
28 | ...item,
29 | active: idx === index,
30 | }));
31 | setItems(newItems);
32 | };
33 | const handleTabClose = (tab) => {
34 | const index = items.findIndex((i, k) => k === tab);
35 | const newItems = items.map((item, idx) => ({
36 | ...item,
37 | active: setActive(idx, index),
38 | }));
39 | newItems.splice(index, 1);
40 | setItems(newItems);
41 | };
42 |
43 | const setActive = (idx, index) => {
44 | if (idx === index) {
45 | return false;
46 | } else if (idx === index - 1) {
47 | return index + 1 <= items.length - 1 ? false : true;
48 | } else if (idx === index + 1) {
49 | return true;
50 | } else {
51 | return false;
52 | }
53 | };
54 |
55 | const handleTabRename = (tab, event) => {
56 | if (!event.target.classList.contains("tab-title")) return;
57 |
58 | const index = items.findIndex((i, k) => k === tab);
59 | const currentTabTitleEl = event.target;
60 | const currentTab = items[tab];
61 | const initialName = currentTab.title;
62 | const tabExt = currentTab.ext;
63 |
64 | const form = document.createElement("form");
65 | currentTabTitleEl.replaceChildren(form);
66 | const inputField = document.createElement("input");
67 | inputField.value = initialName;
68 | form.appendChild(inputField);
69 | const currentTabChild = currentTabTitleEl.firstChild;
70 | currentTabChild[0].focus();
71 | currentTabChild[0].select();
72 |
73 | currentTabChild.addEventListener("submit", tabRenameSubmitHandler, {
74 | once: true,
75 | });
76 | currentTabChild.addEventListener("focusout", tabRenameFocusHandler, {
77 | once: true,
78 | });
79 |
80 | function tabRenameSubmitHandler(e) {
81 | currentTabChild.removeEventListener("focusout", tabRenameFocusHandler);
82 | e.preventDefault();
83 | const newName = e.target[0].value;
84 | setTabName(newName, tabExt);
85 | }
86 |
87 | function tabRenameFocusHandler(e) {
88 | currentTabChild.removeEventListener("submit", tabRenameSubmitHandler);
89 | const currentName = e.target.value;
90 | setTabName(currentName, tabExt);
91 | }
92 |
93 | function setTabName(name, ext) {
94 | const newItems = items.map((item, idx) => ({
95 | ...item,
96 | title: idx === index ? name : item.title,
97 | }));
98 | setItems(newItems);
99 | const extEl = document.createElement("span");
100 | extEl.textContent = ext;
101 | extEl.classList.add(
102 | ext === ".js"
103 | ? "text-yellow-500"
104 | : ext === ".css"
105 | ? "text-blue-500"
106 | : ext === ".html"
107 | ? "text-orange-500"
108 | : ext === ".p2p"
109 | ? "text-[#5F5BD7]"
110 | : "untitled"
111 | );
112 | currentTabTitleEl.replaceChildren(name, extEl);
113 | }
114 | };
115 |
116 | useEffect(() => {
117 | setTimeout(() => {
118 | setErrorMessage("");
119 | }, 6000);
120 | }, [errorMessage]);
121 |
122 | return (
123 | <>
124 |
125 | setErrorMessage("")}
129 | />
130 |
131 |
132 |
133 |
134 |
135 | {
137 | handleButtonClicks(i);
138 | }}
139 | />
140 |
141 |
142 | {
145 | event.stopPropagation();
146 | handleTabActive(i);
147 | }}
148 | handleCloseTab={(i, event) => {
149 | event.stopPropagation();
150 | handleTabClose(i);
151 | }}
152 | handleRenameTab={(i, event) => {
153 | event.stopPropagation();
154 | handleTabRename(i, event);
155 | }}
156 | />
157 |
158 |
159 |
160 | <>
161 | {view == "quicklinks" ? (
162 |
{
164 | router.push("/");
165 | }}
166 | >
167 | {
169 | router.push("/");
170 | }}
171 | />
172 |
173 | ) : view == "login" ? (
174 |
{
176 | router.push("/");
177 | }}
178 | >
179 | {
181 | router.push("/");
182 | }}
183 | />
184 |
185 | ) : view == "signup" ? (
186 |
{
188 | router.push("/");
189 | }}
190 | >
191 | {
193 | router.push("/");
194 | }}
195 | />
196 |
197 | ) : view == "recoveraccount" ? (
198 |
{
200 | router.push("/");
201 | }}
202 | >
203 |
204 |
205 | ) : view === "verificationOverlay" ? (
206 |
{
208 | router.push("/");
209 | }}
210 | >
211 | {
213 | router.push("/");
214 | }}
215 | />
216 |
217 | ) : view === "verificationSuccessful" ? (
218 |
{
220 | router.push("/");
221 | }}
222 | >
223 | {
225 | router.push("/");
226 | }}
227 | />
228 |
229 | ) : (
230 |
231 | )}
232 |
233 | {items.map((item) => {
234 | if (item?.active && item.title === "Welcome") {
235 | return (
236 |
237 |
238 |
239 | );
240 | } else if (item.active && item.title == "collab") {
241 | return (
242 |
243 |
244 |
245 | );
246 | } else if (item.active && item.title != "Welcome") {
247 | return
;
248 | }
249 | })}
250 | >
251 |
252 |
253 | >
254 | );
255 | }
256 |
257 | export default Home;
258 |
--------------------------------------------------------------------------------
/development/src/components/userLogin_comp.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import { OnboardingHeader } from "./onboardingHeader";
4 | import Link from "next/link";
5 | import Image from "next/image";
6 | import googleIcon from "../../public/assets/onboardingIcons/google.png";
7 | import github_lightMode from "../../public/assets/onboardingIcons/github_lightMode.png";
8 | import github_darkMode from "../../public/assets/onboardingIcons/github_darkMode.png";
9 | import { useTheme } from "next-themes";
10 | import UserLogin from "@/composables/userLoginFunction";
11 | import {
12 | emailValidator,
13 | passwordValidator,
14 | } from "@/composables/emailPasswordValidator";
15 | import { useGithubSignin } from "@/composables/authGithubSigninPopup";
16 | import { PasswordToggle } from "./passwordToggleFunction";
17 | import closeIcon from "../../public/assets/onboardingIcons/close_light.png";
18 | import closeDark from "../../public/assets/onboardingIcons/closecircledark.png";
19 | import { useGoogleSignin } from "@/composables/authGoogleSigninPoppup";
20 | import ErrorModal from "./errorModal_comp";
21 | import { useRouter } from "next/navigation";
22 |
23 | function UserLoginComp({ onClose }) {
24 | const { signinWithGithub, githubError } = useGithubSignin();
25 | const { signinWithGoogle, googleError } = useGoogleSignin();
26 | const [errorMessage, setErrorMessage] = useState("");
27 |
28 | useEffect(() => {
29 | setErrorMessage(githubError || googleError);
30 | if (errorMessage !== "") {
31 | setTimeout(() => {
32 | setErrorMessage("");
33 | }, 6000);
34 | }
35 | }, [githubError, googleError]);
36 |
37 | const handleClose = () => {
38 | setErrorMessage("");
39 | };
40 |
41 | const { theme, setTheme } = useTheme();
42 | const [emailAddress, setEmailAddress] = useState("");
43 | const [password, setPassword] = useState("");
44 | const [emailError, setEmailError] = useState("");
45 | const [passwordError, setPasswordError] = useState("");
46 | const [loginError, setLoginError] = useState("");
47 | const router = useRouter();
48 | const [closeLogin, setCloseLogin] = useState(false);
49 |
50 | const emailChange = (e) => {
51 | setEmailAddress(e.target.value);
52 | setEmailError(false);
53 | };
54 |
55 | const passwordChange = (e) => {
56 | setPassword(e.target.value);
57 | setPasswordError(false);
58 | };
59 |
60 | const handleCloseLogin = () => {
61 | setCloseLogin(true);
62 | router.push("/");
63 | };
64 |
65 | const loginUser = async (e) => {
66 | e.preventDefault();
67 |
68 | const validEmail = emailValidator(emailAddress);
69 | const validPassword = passwordValidator(password);
70 |
71 | if (validEmail === true && validPassword === true) {
72 | try {
73 | const result = await UserLogin(emailAddress, password);
74 | if (result.loggedIn) {
75 | console.log("Logged in", result.message);
76 | handleCloseLogin();
77 | } else {
78 | setErrorMessage(result.message);
79 | }
80 | } catch (error) {
81 | let customMessage;
82 | if (error.code === "auth/user-not-found") {
83 | customMessage = "No user with this email found.";
84 | } else if (error.code === "auth/wrong-password") {
85 | customMessage = "Wrong password provided.";
86 | } else if (error.code === "auth/too-many-requests") {
87 | customMessage =
88 | "Too many unsuccessful login attempts. Please try again later.";
89 | } else {
90 | customMessage = "An error occurred during login.";
91 | }
92 | setErrorMessage(customMessage);
93 | }
94 | } else {
95 | if (validEmail !== true) {
96 | setEmailError(validEmail);
97 | } else {
98 | setEmailError("");
99 | }
100 |
101 | if (validPassword !== true) {
102 | setPasswordError(validPassword);
103 | } else {
104 | setPasswordError("");
105 | }
106 | }
107 | };
108 |
109 | return (
110 |
234 | );
235 | }
236 | export default UserLoginComp;
237 |
--------------------------------------------------------------------------------
/development/src/components/webcam_comp.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useRef, useState } from "react";
2 |
3 | export default function WebCamRecorder({ onBlobChanged, peername, isUser }) {
4 | const videoRef = useRef(null);
5 | const [videoStream, setVideoStream] = useState(null);
6 | const [audioStream, setAudioStream] = useState(null);
7 | const [recorder, setRecorder] = useState(null);
8 | const [isRecording, setIsRecording] = useState(false);
9 | const [audioEnabled, setAudioEnabled] = useState(false);
10 | const [videoEnabled, setVideoEnabled] = useState(false);
11 | const [isSession, setIsSession] = useState(false);
12 | const [blob, setBlob] = useState(false);
13 |
14 | const stopAudio = () => {
15 | try {
16 | audioStream.getTracks().forEach((track) => track.stop());
17 | setAudioStream(null);
18 | setAudioEnabled(false);
19 | } catch (error) {
20 | console.log(error);
21 | }
22 | };
23 | const micActivate = () => {
24 | try {
25 | if (audioStream && audioEnabled) {
26 | stopAudio();
27 | } else {
28 | if (audioStream == null) {
29 | setAudioEnabled(true);
30 | } else if (!audioEnabled) {
31 | stopAudio();
32 | }
33 | }
34 | } catch (error) {
35 | console.log(error);
36 | }
37 | };
38 | const stopVideoCam = () => {
39 | try {
40 | videoStream.getTracks().forEach((track) => track.stop());
41 | setVideoStream(null);
42 | } catch (error) {
43 | console.log(error);
44 | }
45 | };
46 |
47 | const videoActivate = () => {
48 | try {
49 | if (videoStream && videoEnabled) {
50 | stopVideoCam();
51 | setVideoEnabled(!videoEnabled);
52 | setVideoStream(null);
53 | } else {
54 | startVideoStream();
55 | setVideoEnabled(!videoEnabled);
56 | }
57 | } catch (error) {
58 | console.log(error);
59 | }
60 | };
61 | const startAudioStream = useCallback(() => {
62 | navigator.mediaDevices
63 | .getUserMedia({ audio: true })
64 | .then((stream) => {
65 | setAudioStream(stream);
66 | })
67 | .catch((err) => console.error(err));
68 | }, []);
69 | const startVideoStream = useCallback(() => {
70 | navigator.mediaDevices
71 | .getUserMedia({ video: true })
72 | .then((stream) => {
73 | videoRef.current.srcObject = stream;
74 | setVideoStream(stream);
75 | })
76 | .catch((err) => console.error(err));
77 | }, [videoEnabled]);
78 | const makeRecording = () => {
79 | if (isRecording) {
80 | stopRecording();
81 | } else {
82 | startRecording();
83 | }
84 | };
85 | const startRecording = () => {
86 | if (audioStream && videoStream) {
87 | let mediaStream = new MediaStream();
88 | videoStream.getTracks().forEach((track) => mediaStream.addTrack(track));
89 | audioStream.getTracks().forEach((track) => mediaStream.addTrack(track));
90 |
91 | const options = { mimeType: "video/webm" };
92 | const newRecorder = new MediaRecorder(mediaStream, options);
93 | setRecorder(newRecorder);
94 | setIsRecording(true);
95 |
96 | const chunks = [];
97 | newRecorder.ondataavailable = (event) => {
98 | chunks.push(event.data);
99 | };
100 | newRecorder.onstop = () => {
101 | const blob = new Blob(chunks, { type: "video/webm" });
102 | onBlobChanged(blob);
103 | setBlob(true);
104 | setAudioEnabled(false);
105 | setVideoEnabled(false);
106 | };
107 | newRecorder.start();
108 | } else {
109 | console.log(audioStream, videoStream);
110 | console.log("one or more streams are not active");
111 | }
112 | };
113 |
114 | const stopRecording = () => {
115 | if (recorder && recorder.state !== "inactive") {
116 | recorder.stop();
117 | setIsRecording(false);
118 | }
119 | };
120 | useEffect(() => {
121 | if (audioEnabled) {
122 | startAudioStream();
123 | } else {
124 | if (audioStream != null) {
125 | stopAudio();
126 | }
127 | }
128 | if (videoEnabled) {
129 | console.log("Enabled");
130 | startVideoStream();
131 | } else {
132 | if (videoStream != null) {
133 | stopVideoCam();
134 | }
135 | }
136 | if (audioEnabled && videoEnabled) {
137 | setIsSession(true);
138 | } else {
139 | setIsSession(false);
140 | }
141 | }, [audioEnabled, videoEnabled]);
142 |
143 | return (
144 |
145 |
155 |
156 |
157 | {isUser ? (
158 |
159 |
199 |
238 | {isSession ? (
239 |
281 | ) : (
282 |
283 | )}
284 |
285 | ) : (
286 |
287 | )}
288 |
293 |
{peername ? peername : "Awaiting user..."}
294 |
295 |
296 |
297 |
298 | );
299 | }
300 |
--------------------------------------------------------------------------------
/development/src/components/signup_comp.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import { OnboardingHeader } from "./onboardingHeader";
4 | import Link from "next/link";
5 | import Image from "next/image";
6 | import githubIcon from "../../public/assets/onboardingIcons/github.png";
7 | import githubDark from "../../public/assets/onboardingIcons/github_black.png";
8 | import googleIcon from "../../public/assets/onboardingIcons/google.png";
9 | import { PasswordToggle } from "./passwordToggleFunction";
10 | import { useGithubSignin } from "@/composables/authGithubSigninPopup";
11 | import { useGoogleSignin } from "@/composables/authGoogleSigninPoppup";
12 | import { signupFormValidation } from "@/composables/signupFormValidation";
13 | import { authSignUp } from "@/composables/authSignupFunction";
14 | import { useTheme } from "next-themes";
15 | import closeIcon from "../../public/assets/onboardingIcons/close_light.png";
16 | import closeIconDark from "../../public/assets/onboardingIcons/closecircledark.png";
17 | import ErrorModal from "./errorModal_comp";
18 | import { getDocs, collection, where, query } from "firebase/firestore";
19 | import { appFirestore } from "../composables/firebaseConfig/config";
20 | import VerificationOverlay from "./VerificationOverlay";
21 | import { useRouter } from "next/navigation";
22 |
23 | function SignUpComponent() {
24 | const { signinWithGithub, githubError } = useGithubSignin();
25 | const { signinWithGoogle, googleError } = useGoogleSignin();
26 | const [errorMessage, setErrorMessage] = useState("");
27 | const router = useRouter();
28 | useEffect(() => {
29 | setErrorMessage(githubError || googleError);
30 | if (errorMessage !== "") {
31 | setTimeout(() => {
32 | setErrorMessage("");
33 | }, 6000);
34 | }
35 | }, [githubError, googleError]);
36 |
37 | const handleClose = () => {
38 | setErrorMessage("");
39 | };
40 |
41 | const { theme, setTheme } = useTheme();
42 | const [user, setUser] = useState({
43 | firstname: "",
44 | lastname: "",
45 | email: "",
46 | username: "",
47 | password: "",
48 | confirm_password: "",
49 | });
50 |
51 | const [formSubmitted, setFormSubmitted] = useState(false);
52 | const [errors, setErrors] = useState({});
53 | const [usernameAvailable, setUsernameAvailable] = useState(null);
54 | const [showVerificationOverlay, setShowVerificationOverlay] = useState(false);
55 | const [showForm, setShowForm] = useState(true);
56 |
57 | const handleCloseForm = () => {
58 | setShowForm(false);
59 | router.push("/");
60 | };
61 |
62 | const handleChange = async (e) => {
63 | const { name, value } = e.target;
64 | setUser((prevUser) => ({ ...prevUser, [name]: value }));
65 |
66 | setErrors((prevErrors) => ({ ...prevErrors, [name]: "" }));
67 |
68 | if (name === "username") {
69 | try {
70 | const collectionRef = query(
71 | collection(appFirestore, "CODERS"),
72 | where("username", "==", `${value}`)
73 | );
74 | const querySnapshot = await getDocs(collectionRef);
75 |
76 | if (querySnapshot.empty) {
77 | setUsernameAvailable(true);
78 | } else {
79 | setUsernameAvailable(false);
80 | }
81 | } catch (error) {
82 | console.log(error);
83 | }
84 | }
85 | };
86 |
87 | const handleSubmit = async (e) => {
88 | e.preventDefault();
89 | const { firstname, lastname, email, password, username } = user;
90 | const formErrors = signupFormValidation(user);
91 |
92 | if (Object.keys(formErrors).length === 0) {
93 | try {
94 | const signUpUser = await authSignUp(
95 | firstname,
96 | lastname,
97 | email,
98 | password,
99 | username
100 | );
101 |
102 | if (signUpUser.success) {
103 | const createdUser = signUpUser.user;
104 | setFormSubmitted(true);
105 | setShowVerificationOverlay(true);
106 |
107 | return createdUser;
108 | } else {
109 | setErrors({ firebaseError: signUpUser.error });
110 | }
111 | } catch (error) {
112 | setErrors({ firebaseError: error.message });
113 | }
114 | } else {
115 | setErrors(formErrors);
116 | }
117 | };
118 |
119 | return (
120 |
121 | {showForm ? (
122 |
392 | ) : null}
393 |
394 | {showVerificationOverlay && (
395 |
setShowVerificationOverlay(false)}
398 | />
399 | )}
400 |
401 | handleClose()}
405 | />
406 |
407 | );
408 | }
409 |
410 | export default SignUpComponent;
411 |
--------------------------------------------------------------------------------
/development/public/assets/dashboard/welcome_comp.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------