joining and leaving backend side done and now agora test here we go

This commit is contained in:
Arjun Patel
2022-02-07 21:57:17 -08:00
parent 8d382f960a
commit 1bc7957824
8 changed files with 235 additions and 26 deletions
+2 -4
View File
@@ -13,7 +13,7 @@ import AgoraRTC, {
import { getFunctions, httpsCallable } from "firebase/functions";
import toast from "react-hot-toast";
const config: ClientConfig = {
export const config: ClientConfig = {
mode: "rtc",
codec: "vp8",
};
@@ -22,7 +22,7 @@ const config: ClientConfig = {
// const useMicrophoneTracks = createMicrophoneAudioTrack();
// TODO: move this to env file
const appId: string = "c8dfd65deb5c4741bd564085627139d0"; //ENTER APP ID HERE
export const appId: string = "c8dfd65deb5c4741bd564085627139d0"; //ENTER APP ID HERE
export default class AgoraService {
private functions = getFunctions();
@@ -90,5 +90,3 @@ export default class AgoraService {
// }
// }
}
export { appId };
@@ -8,6 +8,8 @@ import {
setDoc,
runTransaction,
writeBatch,
arrayUnion,
arrayRemove,
} from "firebase/firestore";
import Conversation, {
AudioClip,
@@ -179,7 +181,33 @@ export default class ConversationService {
},
{ merge: true }
);
}
return false;
async joinLiveConversation(convoId: string, userJoiningId: string) {
// todo: make sure that user is part of the convo or valid in it
const docRef = doc(this.db, Collections.conversations, convoId);
await setDoc(
docRef,
{
membersInLiveRoom: arrayUnion(userJoiningId),
lastActivityDate: serverTimestamp(),
lastUpdatedBy: userJoiningId,
},
{ merge: true }
);
}
async leaveLiveConversation(convoId: string, userJoiningId: string) {
// todo: make sure that user is part of the convo or valid in it
const docRef = doc(this.db, Collections.conversations, convoId);
await setDoc(
docRef,
{
membersInLiveRoom: arrayRemove(userJoiningId),
lastActivityDate: serverTimestamp(),
lastUpdatedBy: userJoiningId,
},
{ merge: true }
);
}
}
+3
View File
@@ -1,3 +1,4 @@
import AgoraService from "./agoraService";
import CloudStorageService from "./cloudStorageService";
import ConversationService from "./conversationService";
import UserService from "./userService";
@@ -7,3 +8,5 @@ export const conversationService = new ConversationService();
export const userService = new UserService();
export const cloudStorageService = new CloudStorageService();
export const agoraService = new AgoraService();
@@ -28,6 +28,8 @@ import { Routes } from "@nirvana/common/helpers/routes";
export default function ConversationFullRow(props: {
conversation: Conversation;
handleJoinLive: (conversationId: string) => Promise<void>;
handleLeaveLive: (conversationId: string) => Promise<void>;
}) {
const { currUser } = useAuth();
const usersConvosMap = useRecoilValue(allUsersConversationsAtom);
@@ -75,10 +77,6 @@ export default function ConversationFullRow(props: {
audio.play();
};
const joinLive = () => {
toast("connecting");
};
/**
* show actions based on which state it is currently in for this user
*/
@@ -134,7 +132,7 @@ export default function ConversationFullRow(props: {
<span
onClick={(e) => {
e.stopPropagation();
joinLive();
props.handleJoinLive(props.conversation.id);
}}
className="p-2 rounded-full hover:cursor-pointer hover:bg-slate-200"
>
@@ -164,7 +162,7 @@ export default function ConversationFullRow(props: {
isNewIncoming ? "text-slate-400 font-semibold" : "text-slate-300"
}`}
>
{moment(props.conversation.lastActivityDate.toDate()).fromNow()}
{moment(props.conversation.lastActivityDate?.toDate()).fromNow()}
</span>
{/* actions */}
@@ -1,9 +1,18 @@
import Conversation from "@nirvana/common/models/conversation";
import { agoraService, conversationService } from "@nirvana/common/services";
import { Avatar, Tooltip } from "antd";
import { FaWalking } from "react-icons/fa";
import toast from "react-hot-toast";
import { FaPhoneSlash, FaWalking } from "react-icons/fa";
import { useAuth } from "../../contexts/authContext";
import { MasterAvatarGroupWithUserFetch } from "../UserDetails/MasterAvatarGroup";
export default function LiveRoom(props: { conversation: Conversation }) {
export default function LiveRoom(props: {
conversation: Conversation;
handleJoinLive: (conversationId: string) => Promise<void>;
handleLeaveLive: (conversationId: string) => Promise<void>;
}) {
const { currUser } = useAuth();
return (
<span className="group relative flex flex-row items-center bg-slate-50 rounded-lg border p-5 animate-pulse">
<MasterAvatarGroupWithUserFetch
@@ -19,14 +28,27 @@ export default function LiveRoom(props: { conversation: Conversation }) {
{"01:20"}
</span>
<Tooltip title={"Join live conversation"}>
<span
className="p-2 rounded-full hover:cursor-pointer hover:bg-slate-200
{props.conversation.membersInLiveRoom.includes(currUser!.uid) ? (
<Tooltip title={"Leave"}>
<span
onClick={() => props.handleLeaveLive(props.conversation.id)}
className="p-2 rounded-full hover:cursor-pointer hover:bg-slate-200
absolute right-5 text-slate-50 group-hover:text-red-600 text-lg transition-all -z-10 group-hover:z-10"
>
<FaPhoneSlash className="text-lg" />
</span>
</Tooltip>
) : (
<Tooltip title={"Join live conversation"}>
<span
onClick={() => props.handleJoinLive(props.conversation.id)}
className="p-2 rounded-full hover:cursor-pointer hover:bg-slate-200
absolute right-5 text-slate-50 group-hover:text-teal-600 text-lg transition-all -z-10 group-hover:z-10"
>
<FaWalking className="text-lg" />
</span>
</Tooltip>
>
<FaWalking className="text-lg" />
</span>
</Tooltip>
)}
</span>
);
}
@@ -0,0 +1,3 @@
export default function LiveRoomsHandler() {
return <></>;
}
@@ -24,11 +24,139 @@ import {
RelativeTimeSeparatedConvosSelector,
} from "../../recoil/main";
import ConversationFullRow from "../Conversations/ConversationFullRow";
import LiveRooms from "../LiveRooms/LiveRoomsHandler";
import LiveRoomsHandler from "../LiveRooms/LiveRoomsHandler";
import {
ClientConfig,
IAgoraRTC,
IAgoraRTCClient,
IMicrophoneAudioTrack,
} from "agora-rtc-sdk-ng";
import { useEffect, useState } from "react";
import { config, appId } from "@nirvana/common/services/agoraService";
import toast from "react-hot-toast";
import { agoraService, conversationService } from "@nirvana/common/services";
import { useAuth } from "../../contexts/authContext";
export default function Conversations() {
const router = useRouter();
const { currUser } = useAuth();
const liveRooms = useRecoilValue(liveRoomsSelector);
const [agoraRtc, setAgoraRtc] = useState<IAgoraRTC>();
const [agoraRtcClient, setAgoraRtcClient] = useState<IAgoraRTCClient>();
const [localAudioTrack, setLocalAudioTrack] =
useState<IMicrophoneAudioTrack>();
// set up agora stuff
useEffect(() => {
(async function () {
// dynamic import as the server side import doesn't work
const AgoraRTC = (await import("agora-rtc-sdk-ng")).default;
const agoraClient = AgoraRTC.createClient(config);
setAgoraRtcClient(agoraClient);
setAgoraRtc(AgoraRTC);
})();
}, []);
async function handleJoinChannel(channelName: string, agoraToken: string) {
const localTrack: IMicrophoneAudioTrack =
await agoraRtc!.createMicrophoneAudioTrack();
setLocalAudioTrack(localTrack);
const init = async (chanName: string) => {
agoraRtcClient!.on("user-published", async (user, mediaType) => {
await agoraRtcClient!.subscribe(user, mediaType);
console.log("subscribe success");
if (mediaType === "audio") {
user.audioTrack?.play();
}
});
agoraRtcClient!.on("user-unpublished", async (user, type) => {
console.log("unpublished", user, type);
if (type === "audio") {
user.audioTrack?.stop();
}
await agoraRtcClient!.unsubscribe(user);
});
agoraRtcClient!.on("user-left", (user) => {
console.log("user left", user);
});
await agoraRtcClient!.join(appId, chanName, agoraToken, null);
if (localTrack) await agoraRtcClient!.publish(localTrack);
};
if (localTrack) {
console.log("init ready");
init(channelName);
} else {
toast.error("Not ready for joining call");
return;
}
}
async function handleLeaveChannel() {
// destroy local track
localAudioTrack?.close();
// leave all channels
await agoraRtcClient!.leave();
}
// click on join
// add me to the active list of people in the call
// update the conversation lastActivityDate as well
const handleJoinLive = async (conversationId: string) => {
const connectingToast = toast.loading("connecting");
try {
// agora token from CF
const agoraToken = await agoraService.getAgoraToken(conversationId);
console.log(agoraToken);
await conversationService.joinLiveConversation(
conversationId,
currUser!.uid
);
} catch {
toast.error("problem in joining");
}
toast.remove(connectingToast);
};
const handleLeaveLive = async (conversationId: string) => {
const leavingToast = toast.loading("leaving");
try {
await conversationService.leaveLiveConversation(
conversationId,
currUser!.uid
);
} catch {
toast.error("problem in joining");
}
toast.remove(leavingToast);
};
// agora start the call stream
// leave the call on before unload if in a call
// update the database to remove my userId from the list of people in the room
const relativeConvoSections = useRecoilValue(
RelativeTimeSeparatedConvosSelector
);
@@ -68,7 +196,7 @@ export default function Conversations() {
return (
<>
{/* live */}
{/* live rooms */}
{liveRooms?.length > 0 && (
<>
<span className="flex flex-row items-center mb-5 px-5 w-full">
@@ -81,7 +209,12 @@ export default function Conversations() {
{/* row of live room cards */}
<span className="flex flex-col w-full items-stretch">
{liveRooms.map((lRoom) => (
<LiveRoom key={lRoom.id} conversation={lRoom} />
<LiveRoom
key={lRoom.id}
conversation={lRoom}
handleJoinLive={handleJoinLive}
handleLeaveLive={handleLeaveLive}
/>
))}
</span>
</>
@@ -97,7 +230,12 @@ export default function Conversations() {
</span>
<span className="flex flex-col w-full items-stretch">
{todayConvos.map((tRoom) => (
<ConversationFullRow key={tRoom.id} conversation={tRoom} />
<ConversationFullRow
key={tRoom.id}
conversation={tRoom}
handleJoinLive={handleJoinLive}
handleLeaveLive={handleLeaveLive}
/>
))}
</span>
</>
@@ -113,7 +251,12 @@ export default function Conversations() {
</span>
<span className="flex flex-col w-full items-stretch">
{last7DaysConvos.map((tRoom) => (
<ConversationFullRow key={tRoom.id} conversation={tRoom} />
<ConversationFullRow
key={tRoom.id}
conversation={tRoom}
handleJoinLive={handleJoinLive}
handleLeaveLive={handleLeaveLive}
/>
))}
</span>
</>
@@ -129,7 +272,12 @@ export default function Conversations() {
</span>
<span className="flex flex-col w-full items-stretch">
{earlierMonthConvos.map((tRoom) => (
<ConversationFullRow key={tRoom.id} conversation={tRoom} />
<ConversationFullRow
key={tRoom.id}
conversation={tRoom}
handleJoinLive={handleJoinLive}
handleLeaveLive={handleLeaveLive}
/>
))}
</span>
</>
@@ -145,7 +293,12 @@ export default function Conversations() {
</span>
<span className="flex flex-col w-full items-stretch">
{oldJunkConvos.map((tRoom) => (
<ConversationFullRow key={tRoom.id} conversation={tRoom} />
<ConversationFullRow
key={tRoom.id}
conversation={tRoom}
handleJoinLive={handleJoinLive}
handleLeaveLive={handleLeaveLive}
/>
))}
</span>
</>
+4
View File
@@ -384,6 +384,10 @@ export const checkIncomingMessageSelector = selectorFamily({
return true;
}
if (!currConvo?.lastActivityDate) {
return false;
}
if (
currConvo.lastActivityDate.toDate() >
currUserConvoAssoc.lastInteractionDate?.toDate()