├── 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 | 2 | 3 | 4 | 5 | 6 | 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 | 15 | 19 |
20 | {children} 21 |
22 |
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 | nav_btn_iconadd 17 | 18 |
19 | ); 20 | } 21 | 22 | export default SideTopNavControl; 23 | -------------------------------------------------------------------------------- /development/public/assets/onboardingIcons/eyes.js: -------------------------------------------------------------------------------- 1 | const eyes = () => ( 2 | 10 | 15 | 20 | 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 | 10 | 15 | 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 | 2 | 3 | 4 | 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 | 2 | 3 | 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 | welcome 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 | close icon 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 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /development/public/assets/onboardingIcons/closecirclelight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 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 | close Icon 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 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | 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 | {ext} 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 | close Icon 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 |
81 | 86 | Verify Email 87 | 88 |
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 | language-icon 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 | language-icon 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 | language-icon 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 | language-icon 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 |
71 | 77 | { 87 | if (emailValidator(e.target.value)) { 88 | setError(false); 89 | setEmailAddress(e.target.value); 90 | } else { 91 | setError(true); 92 | } 93 | }} 94 | /> 95 | {error && ( 96 | {errorMessage} 97 | )} 98 | {success && ( 99 | {successMessage} 100 | )} 101 | 107 |
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 | close Icon 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 |
104 | 109 | Verify Email 110 | 111 |
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 | 36 | 40 | 44 | 48 | 52 | 56 | 57 | ); 58 | } else { 59 | return ( 60 | 67 | 71 | 75 | 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 | html 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 | language-icon 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 | language-icon 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 | 37 | 42 | 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 |
114 |
115 |
116 | 120 | 121 | 131 |
132 | 133 |
134 | 145 | 161 |
162 | 163 |
164 |
165 |

OR

166 |
167 |
168 | 169 |
170 |
171 | 172 | 184 | {emailError &&

{emailError}

} 185 | {loginError &&

{loginError}

} 186 |
187 | 188 |
189 | 190 | 200 | {passwordError && ( 201 |

{passwordError}

202 | )} 203 | 204 | Forgot password? 205 | 206 |
207 | 208 |
209 | 215 | 216 |

217 | {"Don't have an account with us?"} 218 | 222 | Create your account 223 | 224 |

225 |
226 |
227 |
228 | handleClose()} 232 | /> 233 | 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 |
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 |
126 |
127 | 131 | close icon 137 |
138 | 139 |
140 | 155 | 156 | 172 |
173 | 174 |
175 |
176 |

OR

177 |
178 |
179 | 180 |
181 |
182 |
183 | 189 | 201 | {errors.firstname && ( 202 | 203 | {errors.firstname} 204 | 205 | )} 206 |
207 | 208 |
209 | 215 | 227 | {errors.lastname && ( 228 | 229 | {errors.lastname} 230 | 231 | )} 232 |
233 |
234 | 235 |
236 |
237 | 243 | 257 | {errors.email && ( 258 | 259 | {errors.email} 260 | 261 | )} 262 | {errors.firebaseError && ( 263 | 264 | Email already in use 265 | 266 | )} 267 |
268 | 269 |
270 | 276 | 293 | {errors.username && ( 294 | 295 | {errors.username} 296 | 297 | )} 298 | {!errors.username && 299 | !usernameAvailable && 300 | user.username !== "" && ( 301 | 302 | Username is not available 303 | 304 | )} 305 | {!errors.username && 306 | usernameAvailable && 307 | user.username !== "" && ( 308 | 309 | Username available 310 | 311 | )} 312 |
313 |
314 | 315 |
316 |
321 | 327 | 338 | {errors.password && ( 339 | 340 | {errors.password} 341 | 342 | )} 343 |
344 | 345 |
346 | 352 | 364 | {errors.confirm_password && ( 365 | 366 | {errors.confirm_password} 367 | 368 | )} 369 |
370 |
371 |
372 | 373 |
374 | 380 | 381 |

382 | Already have an account? 383 | 387 | Log in 388 | 389 |

390 |
391 |
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 | --------------------------------------------------------------------------------