diff --git a/components/OfficeCard.tsx b/components/OfficeCard.tsx index b7865eb..59054e4 100644 --- a/components/OfficeCard.tsx +++ b/components/OfficeCard.tsx @@ -7,6 +7,11 @@ import OfficeRoom, { OfficeRoomState } from "../models/officeRoom"; import { User } from "../models/user"; import OfficeRoomService from "../services/officeRoomService"; import { VscDebugDisconnect } from "react-icons/vsc"; +import { + AgoraVideoPlayer, + createClient, + createMicrophoneAudioTrack, +} from "agora-rtc-react"; interface IOfficeCard { officeRoom: OfficeRoom; @@ -31,6 +36,8 @@ export default function OfficeCard(props: IOfficeCard) { }, [] as User[]); async function handleJoinOfficeRoom() { + toast.loading("Connecting"); + // make sure user is not in another room // get agora token from cloud function // join channel for agora @@ -41,15 +48,24 @@ export default function OfficeCard(props: IOfficeCard) { } try { - const newMembers = [...props.officeRoom.members, currUser.uid]; - await officeRoomService.updateMembersInOfficeRoom( - props.officeRoom.id, - newMembers - ); + // todo: pass in the right channel name based on the office room id + const agoraToken = await officeRoomService.getAgoraToken(); + + console.log(agoraToken); + + // join right channel on agora + + // const newMembers = [...props.officeRoom.members, currUser.uid]; + // await officeRoomService.updateMembersInOfficeRoom( + // props.officeRoom.id, + // newMembers + // ); } catch (error) { console.error(error); toast.error("problem joining office room"); } + + toast.dismiss(); } async function handleLeaveOfficeRoom() { diff --git a/contexts/authContext.js b/contexts/authContext.js index 7a2d999..f99e78c 100644 --- a/contexts/authContext.js +++ b/contexts/authContext.js @@ -11,6 +11,7 @@ import React, { useContext, useEffect, useState } from "react"; import toast from "react-hot-toast"; import Loading from "../components/Loading"; import firebase from "../services/firebaseService"; +import cookie from "js-cookie"; const AuthContext = React.createContext(null); const googleProvider = new GoogleAuthProvider(); @@ -34,8 +35,10 @@ export function AuthProvider({ children }) { // set the token in the cookies for ssr verification const token = await user.getIdToken(); // nookies.set(undefined, "token", token, { path: "/" }); + cookie.set("auth", token); } else { // nookies.set(undefined, "token", "", { path: "/" }); + cookie.remove("auth"); setCurrUser(null); } diff --git a/package-lock.json b/package-lock.json index 0d934f4..3594f9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,8 @@ "": { "name": "nirvana", "dependencies": { + "agora-rtc-react": "^1.1.0", + "agora-rtc-sdk-ng": "^4.8.1", "antd": "^4.18.3", "firebase": "^9.6.2", "firebase-admin": "^10.0.1", @@ -2582,6 +2584,25 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "optional": true }, + "node_modules/agora-rtc-react": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-1.1.0.tgz", + "integrity": "sha512-+cvK3gXcUC2FzXkrN9L/nE5B6FqgP2w7fWid5wpsdtzmBMf/gehzN1cDhMSDz2oI3Jq/9zo9R4iVswPx+QtAGA==", + "dependencies": { + "agora-rtc-sdk-ng": "^4.3.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17" + } + }, + "node_modules/agora-rtc-sdk-ng": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz", + "integrity": "sha512-hAYb7quX2cUoURnCobYBXtdfjbuec3zuZupkfmroh5IXLihRRNPzEhT51QUuiSxgsAJG2Yr4XB6g6BR0aRGxrg==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -11275,6 +11296,19 @@ } } }, + "agora-rtc-react": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-1.1.0.tgz", + "integrity": "sha512-+cvK3gXcUC2FzXkrN9L/nE5B6FqgP2w7fWid5wpsdtzmBMf/gehzN1cDhMSDz2oI3Jq/9zo9R4iVswPx+QtAGA==", + "requires": { + "agora-rtc-sdk-ng": "^4.3.0" + } + }, + "agora-rtc-sdk-ng": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz", + "integrity": "sha512-hAYb7quX2cUoURnCobYBXtdfjbuec3zuZupkfmroh5IXLihRRNPzEhT51QUuiSxgsAJG2Yr4XB6g6BR0aRGxrg==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", diff --git a/package.json b/package.json index b250d0d..e92c7ca 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "lint": "next lint" }, "dependencies": { + "agora-rtc-react": "^1.1.0", + "agora-rtc-sdk-ng": "^4.8.1", "antd": "^4.18.3", "firebase": "^9.6.2", "firebase-admin": "^10.0.1", diff --git a/pages/api/agora/token.ts b/pages/api/agora/token.ts new file mode 100644 index 0000000..2c49c80 --- /dev/null +++ b/pages/api/agora/token.ts @@ -0,0 +1,25 @@ +import { NextApiRequest, NextApiResponse } from "next"; +// import { adminApp } from "../../../services/firebaseAdminService"; +import { getAuth } from "firebase/auth"; +import firebaseAdminAll, { credential } from "firebase-admin"; + +// todo do this here instead of cloud functions or just leave as is +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + console.log(req.cookies); + console.log(req.body); + + if (!req.cookies.auth) { + res.status(401).send({ reason: "not authorized" }); + return; + } + + // if (!verifiedToken) { + // res.status(401).send({ reason: "auth token not valid" }); + // return; + // } + + res.status(200).json({ name: "John Doe" }); +} diff --git a/pages/api/auth/validateToken.tsx b/pages/api/auth/validateToken.tsx index 25ef9b3..bcae0b2 100644 --- a/pages/api/auth/validateToken.tsx +++ b/pages/api/auth/validateToken.tsx @@ -1,16 +1,16 @@ -// import fbAdm, { credential } from 'firebase-admin'; -// import firebaseAdmin, { App } from 'firebase-admin/app'; +import fbAdm, { credential } from "firebase-admin"; +import firebaseAdmin, { App } from "firebase-admin/app"; -// import { NextApiRequest, NextApiResponse } from 'next'; +import { NextApiRequest, NextApiResponse } from "next"; -// import serviceAccount from '../../../nirvana-for-business-firebase-adminsdk' +// import serviceAccount from "../../../nirvana-for-business-firebase-adminsdk"; // const adminApp = firebaseAdmin.initializeApp({ // credential: credential.cert({ // privateKey: serviceAccount.private_key, // clientEmail: serviceAccount.client_email, // projectId: serviceAccount.project_id, -// }) +// }), // }); // export default async function(req: NextApiRequest, res: NextApiResponse) { diff --git a/services/agoraService.ts b/services/agoraService.ts new file mode 100644 index 0000000..ebb960a --- /dev/null +++ b/services/agoraService.ts @@ -0,0 +1,22 @@ +import { + ClientConfig, + IAgoraRTCRemoteUser, + ICameraVideoTrack, + IMicrophoneAudioTrack, +} from "agora-rtc-sdk-ng"; +import { + AgoraVideoPlayer, + createClient, + createMicrophoneAndCameraTracks, + createMicrophoneAudioTrack, +} from "agora-rtc-react"; + +const config: ClientConfig = { + mode: "rtc", + codec: "vp8", +}; +const useClient = createClient(config); +const useMicrophoneTracks = createMicrophoneAudioTrack(); + +// TODO: move this to env file +const appId: string = "c8dfd65deb5c4741bd564085627139d0"; //ENTER APP ID HERE diff --git a/services/firebaseAdminService.js b/services/firebaseAdminService.js new file mode 100644 index 0000000..5020e4e --- /dev/null +++ b/services/firebaseAdminService.js @@ -0,0 +1,15 @@ +import firebaseAdmin from "firebase-admin/app"; + +import serviceAccount from "../nirvana-for-business-firebase-adminsdk"; + +const adminApp = firebaseAdmin.initializeApp({ + credential: credential.cert({ + privateKey: serviceAccount.private_key, + clientEmail: serviceAccount.client_email, + projectId: serviceAccount.project_id, + }), +}); + +console.log("initialized firebase admin"); + +export { adminApp }; diff --git a/services/officeRoomService.tsx b/services/officeRoomService.tsx index 73cbbc2..90b8ef4 100644 --- a/services/officeRoomService.tsx +++ b/services/officeRoomService.tsx @@ -11,10 +11,16 @@ import { } from "firebase/firestore"; import OfficeRoom, { OfficeRoomState } from "../models/officeRoom"; import { Collections } from "./collections"; +import { getFunctions, httpsCallable } from "firebase/functions"; + +interface IAgoraTokenResult { + data: { token: string }; +} export default class OfficeRoomService { private db: Firestore = getFirestore(); private batch = writeBatch(this.db); + private functions = getFunctions(); async createInitialOfficeRooms(createdByUserId: string, teamId: string) { const entrance = new OfficeRoom("Entrance", teamId, createdByUserId); @@ -58,6 +64,36 @@ export default class OfficeRoomService { // } // } + async getAgoraToken(): Promise { + // const agoraToken = await fetch("/api/agora/token", { + // method: "post", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify({ channelName: "testChannel" }), + // }); + + const cfResult = httpsCallable(this.functions, "agoraToken"); + + return await cfResult({ channelName: "testChannel" }) + .then((result: IAgoraTokenResult) => { + const data = result.data; + + if (!data.token) { + throw new Error("No token retrieved from agora"); + } + + return data.token; + }) + .catch((error) => { + // Getting the Error details. + const code = error.code; + const message = error.message; + const details = error.details; + throw error; + }); + } + async updateMembersInOfficeRoom( officeRoomId: string, newMembersInRoom: string[]