maybe working full?
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
import {
|
||||||
|
ClientConfig,
|
||||||
|
IAgoraRTC,
|
||||||
|
IAgoraRTCClient,
|
||||||
|
IMicrophoneAudioTrack,
|
||||||
|
} from "agora-rtc-sdk-ng";
|
||||||
import { Tooltip } from "antd";
|
import { Tooltip } from "antd";
|
||||||
import {
|
import {
|
||||||
collection,
|
collection,
|
||||||
@@ -7,9 +13,11 @@ import {
|
|||||||
where,
|
where,
|
||||||
} from "firebase/firestore";
|
} from "firebase/firestore";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
import { useAuth } from "../../contexts/authContext";
|
import { useAuth } from "../../contexts/authContext";
|
||||||
import { useTeamDashboardContext } from "../../contexts/teamDashboardContext";
|
import { useTeamDashboardContext } from "../../contexts/teamDashboardContext";
|
||||||
import OfficeRoom from "../../models/officeRoom";
|
import OfficeRoom from "../../models/officeRoom";
|
||||||
|
import { appId } from "../../services/agoraService";
|
||||||
import { Collections } from "../../services/collections";
|
import { Collections } from "../../services/collections";
|
||||||
import OfficeRoomService from "../../services/officeRoomService";
|
import OfficeRoomService from "../../services/officeRoomService";
|
||||||
import OfficeCard from "../OfficeCard";
|
import OfficeCard from "../OfficeCard";
|
||||||
@@ -18,9 +26,18 @@ const db = getFirestore();
|
|||||||
|
|
||||||
const officeRoomService = new OfficeRoomService();
|
const officeRoomService = new OfficeRoomService();
|
||||||
|
|
||||||
|
const config: ClientConfig = {
|
||||||
|
mode: "rtc",
|
||||||
|
codec: "vp8",
|
||||||
|
};
|
||||||
|
|
||||||
export default function Office() {
|
export default function Office() {
|
||||||
const { currUser } = useAuth();
|
const { currUser } = useAuth();
|
||||||
const { team } = useTeamDashboardContext();
|
const { team } = useTeamDashboardContext();
|
||||||
|
const [agoraRtc, setAgoraRtc] = useState<IAgoraRTC>(null);
|
||||||
|
const [agoraRtcClient, setAgoraRtcClient] = useState<IAgoraRTCClient>(null);
|
||||||
|
const [localAudioTrack, setLocalAudioTrack] =
|
||||||
|
useState<IMicrophoneAudioTrack>(null);
|
||||||
|
|
||||||
const [officeRoomsMap, setOfficeRoomsMap] = useState<Map<string, OfficeRoom>>(
|
const [officeRoomsMap, setOfficeRoomsMap] = useState<Map<string, OfficeRoom>>(
|
||||||
new Map<string, OfficeRoom>()
|
new Map<string, OfficeRoom>()
|
||||||
@@ -66,6 +83,68 @@ export default function Office() {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
let 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();
|
||||||
|
}
|
||||||
|
|
||||||
const allOfficeRooms = Array.from(officeRoomsMap.values());
|
const allOfficeRooms = Array.from(officeRoomsMap.values());
|
||||||
|
|
||||||
allOfficeRooms.sort((a, b) => {
|
allOfficeRooms.sort((a, b) => {
|
||||||
@@ -98,7 +177,12 @@ export default function Office() {
|
|||||||
{/* all office rooms */}
|
{/* all office rooms */}
|
||||||
<span className="flex flex-col overflow-auto pr-2 space-y-2">
|
<span className="flex flex-col overflow-auto pr-2 space-y-2">
|
||||||
{allOfficeRooms.map((officeRoom) => (
|
{allOfficeRooms.map((officeRoom) => (
|
||||||
<OfficeCard key={officeRoom.id} officeRoom={officeRoom} />
|
<OfficeCard
|
||||||
|
key={officeRoom.id}
|
||||||
|
officeRoom={officeRoom}
|
||||||
|
handleJoinChannel={handleJoinChannel}
|
||||||
|
handleLeaveChannel={handleLeaveChannel}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
{/* <OfficeCard /> */}
|
{/* <OfficeCard /> */}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
+24
-16
@@ -7,17 +7,16 @@ import OfficeRoom, { OfficeRoomState } from "../models/officeRoom";
|
|||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import OfficeRoomService from "../services/officeRoomService";
|
import OfficeRoomService from "../services/officeRoomService";
|
||||||
import { VscDebugDisconnect } from "react-icons/vsc";
|
import { VscDebugDisconnect } from "react-icons/vsc";
|
||||||
import {
|
import AgoraService from "../services/agoraService";
|
||||||
AgoraVideoPlayer,
|
|
||||||
createClient,
|
|
||||||
createMicrophoneAudioTrack,
|
|
||||||
} from "agora-rtc-react";
|
|
||||||
|
|
||||||
interface IOfficeCard {
|
interface IOfficeCard {
|
||||||
officeRoom: OfficeRoom;
|
officeRoom: OfficeRoom;
|
||||||
|
handleJoinChannel: Function;
|
||||||
|
handleLeaveChannel: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
const officeRoomService = new OfficeRoomService();
|
const officeRoomService = new OfficeRoomService();
|
||||||
|
const agoraService = new AgoraService();
|
||||||
|
|
||||||
export default function OfficeCard(props: IOfficeCard) {
|
export default function OfficeCard(props: IOfficeCard) {
|
||||||
const { currUser } = useAuth();
|
const { currUser } = useAuth();
|
||||||
@@ -48,36 +47,43 @@ export default function OfficeCard(props: IOfficeCard) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// todo: pass in the right channel name based on the office room id
|
// agora token from CF
|
||||||
const agoraToken = await officeRoomService.getAgoraToken();
|
const agoraToken = await agoraService.getAgoraToken(props.officeRoom.id);
|
||||||
|
|
||||||
console.log(agoraToken);
|
// // function to do it all
|
||||||
|
// await officeRoomService.joinOfficeRoom(props.officeRoom, currUser.uid);
|
||||||
|
|
||||||
// join right channel on agora
|
// handle joining agora channel
|
||||||
|
await props.handleJoinChannel(props.officeRoom.id, agoraToken);
|
||||||
|
|
||||||
// const newMembers = [...props.officeRoom.members, currUser.uid];
|
// update firestore to add ourselves in the office room
|
||||||
// await officeRoomService.updateMembersInOfficeRoom(
|
const newMembers = [...props.officeRoom.members, currUser.uid];
|
||||||
// props.officeRoom.id,
|
await officeRoomService.updateMembersInOfficeRoom(
|
||||||
// newMembers
|
props.officeRoom.id,
|
||||||
// );
|
newMembers
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error("problem joining office room");
|
toast.error("problem joining office room");
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
|
toast.success("joined office room");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleLeaveOfficeRoom() {
|
async function handleLeaveOfficeRoom() {
|
||||||
// leave the channel for agora
|
toast.loading("leaving");
|
||||||
|
|
||||||
// leave from firestore database
|
|
||||||
if (!props.officeRoom.members.includes(currUser.uid)) {
|
if (!props.officeRoom.members.includes(currUser.uid)) {
|
||||||
toast.error("You are not in this office room!");
|
toast.error("You are not in this office room!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// leave agora channel
|
||||||
|
await props.handleLeaveChannel();
|
||||||
|
|
||||||
|
// leave from firestore database
|
||||||
const newMembersInRoom = props.officeRoom.members.filter(
|
const newMembersInRoom = props.officeRoom.members.filter(
|
||||||
(memberId) => memberId != currUser.uid
|
(memberId) => memberId != currUser.uid
|
||||||
);
|
);
|
||||||
@@ -90,6 +96,8 @@ export default function OfficeCard(props: IOfficeCard) {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error("problem leaving office room");
|
toast.error("problem leaving office room");
|
||||||
}
|
}
|
||||||
|
toast.dismiss();
|
||||||
|
toast.success("left office room");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if user is in the office room
|
// check if user is in the office room
|
||||||
|
|||||||
Generated
-23
@@ -6,7 +6,6 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "nirvana",
|
"name": "nirvana",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agora-rtc-react": "^1.1.0",
|
|
||||||
"agora-rtc-sdk-ng": "^4.8.1",
|
"agora-rtc-sdk-ng": "^4.8.1",
|
||||||
"antd": "^4.18.3",
|
"antd": "^4.18.3",
|
||||||
"firebase": "^9.6.2",
|
"firebase": "^9.6.2",
|
||||||
@@ -2584,20 +2583,6 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
"optional": true
|
"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": {
|
"node_modules/agora-rtc-sdk-ng": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz",
|
||||||
@@ -11296,14 +11281,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"agora-rtc-sdk-ng": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.8.1.tgz",
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agora-rtc-react": "^1.1.0",
|
|
||||||
"agora-rtc-sdk-ng": "^4.8.1",
|
"agora-rtc-sdk-ng": "^4.8.1",
|
||||||
"antd": "^4.18.3",
|
"antd": "^4.18.3",
|
||||||
"firebase": "^9.6.2",
|
"firebase": "^9.6.2",
|
||||||
|
|||||||
@@ -1,22 +1,94 @@
|
|||||||
import {
|
import AgoraRTC, {
|
||||||
ClientConfig,
|
ClientConfig,
|
||||||
IAgoraRTCRemoteUser,
|
IAgoraRTCRemoteUser,
|
||||||
ICameraVideoTrack,
|
ICameraVideoTrack,
|
||||||
IMicrophoneAudioTrack,
|
IMicrophoneAudioTrack,
|
||||||
} from "agora-rtc-sdk-ng";
|
} from "agora-rtc-sdk-ng";
|
||||||
import {
|
// import {
|
||||||
AgoraVideoPlayer,
|
// AgoraVideoPlayer,
|
||||||
createClient,
|
// createClient,
|
||||||
createMicrophoneAndCameraTracks,
|
// createMicrophoneAndCameraTracks,
|
||||||
createMicrophoneAudioTrack,
|
// createMicrophoneAudioTrack,
|
||||||
} from "agora-rtc-react";
|
// } from "agora-rtc-react";
|
||||||
|
import { getFunctions, httpsCallable } from "firebase/functions";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
const config: ClientConfig = {
|
const config: ClientConfig = {
|
||||||
mode: "rtc",
|
mode: "rtc",
|
||||||
codec: "vp8",
|
codec: "vp8",
|
||||||
};
|
};
|
||||||
const useClient = createClient(config);
|
|
||||||
const useMicrophoneTracks = createMicrophoneAudioTrack();
|
// const useClient = createClient(config);
|
||||||
|
// const useMicrophoneTracks = createMicrophoneAudioTrack();
|
||||||
|
|
||||||
// TODO: move this to env file
|
// TODO: move this to env file
|
||||||
const appId: string = "c8dfd65deb5c4741bd564085627139d0"; //ENTER APP ID HERE
|
const appId: string = "c8dfd65deb5c4741bd564085627139d0"; //ENTER APP ID HERE
|
||||||
|
|
||||||
|
export default class AgoraService {
|
||||||
|
private functions = getFunctions();
|
||||||
|
// private rtcClient = useClient();
|
||||||
|
|
||||||
|
async getAgoraToken(channelName: string): Promise<string> {
|
||||||
|
const cfResult = httpsCallable(this.functions, "agoraToken");
|
||||||
|
|
||||||
|
return await cfResult({ channelName })
|
||||||
|
.then((result: { data: { token: string } }) => {
|
||||||
|
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 handleJoinChannel(channelName: string, agoraToken: string) {
|
||||||
|
// const localTrack: IMicrophoneAudioTrack =
|
||||||
|
// await AgoraRTC.createMicrophoneAudioTrack();
|
||||||
|
|
||||||
|
// let init = async (name: string) => {
|
||||||
|
// this.rtcClient.on("user-published", async (user, mediaType) => {
|
||||||
|
// await this.rtcClient.subscribe(user, mediaType);
|
||||||
|
// console.log("subscribe success");
|
||||||
|
|
||||||
|
// if (mediaType === "audio") {
|
||||||
|
// user.audioTrack?.play();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// this.rtcClient.on("user-unpublished", async (user, type) => {
|
||||||
|
// console.log("unpublished", user, type);
|
||||||
|
// if (type === "audio") {
|
||||||
|
// user.audioTrack?.stop();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// await this.rtcClient.unsubscribe(user);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// this.rtcClient.on("user-left", (user) => {
|
||||||
|
// console.log("user left", user);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// await this.rtcClient.join(appId, name, agoraToken, null);
|
||||||
|
// if (localTrack) await this.rtcClient.publish(localTrack);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if (localTrack) {
|
||||||
|
// console.log("init ready");
|
||||||
|
// init(channelName);
|
||||||
|
// } else {
|
||||||
|
// toast.error("Not ready for joining call");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { appId };
|
||||||
|
|||||||
@@ -10,17 +10,14 @@ import {
|
|||||||
writeBatch,
|
writeBatch,
|
||||||
} from "firebase/firestore";
|
} from "firebase/firestore";
|
||||||
import OfficeRoom, { OfficeRoomState } from "../models/officeRoom";
|
import OfficeRoom, { OfficeRoomState } from "../models/officeRoom";
|
||||||
|
// import AgoraService from "./agoraService";
|
||||||
import { Collections } from "./collections";
|
import { Collections } from "./collections";
|
||||||
import { getFunctions, httpsCallable } from "firebase/functions";
|
|
||||||
|
|
||||||
interface IAgoraTokenResult {
|
|
||||||
data: { token: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class OfficeRoomService {
|
export default class OfficeRoomService {
|
||||||
private db: Firestore = getFirestore();
|
private db: Firestore = getFirestore();
|
||||||
private batch = writeBatch(this.db);
|
private batch = writeBatch(this.db);
|
||||||
private functions = getFunctions();
|
|
||||||
|
// private agoraService = new AgoraService();
|
||||||
|
|
||||||
async createInitialOfficeRooms(createdByUserId: string, teamId: string) {
|
async createInitialOfficeRooms(createdByUserId: string, teamId: string) {
|
||||||
const entrance = new OfficeRoom("Entrance", teamId, createdByUserId);
|
const entrance = new OfficeRoom("Entrance", teamId, createdByUserId);
|
||||||
@@ -51,47 +48,16 @@ export default class OfficeRoomService {
|
|||||||
await this.batch.commit();
|
await this.batch.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// async createOrUpdateRoom(room: Room) {
|
async joinOfficeRoom(officeRoom: OfficeRoom, userId: string) {
|
||||||
// if (room.id) {
|
// get agoraToken
|
||||||
// //update
|
// const agoraToken = await this.agoraService.getAgoraToken();
|
||||||
// await this.updateRoom(room);
|
|
||||||
// } else {
|
|
||||||
// //create
|
|
||||||
// const roomDocRef = await addDoc(collection(this.db, Collections.rooms), {
|
|
||||||
// ...room,
|
|
||||||
// createdDate: serverTimestamp(),
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async getAgoraToken(): Promise<string> {
|
// join channel
|
||||||
// const agoraToken = await fetch("/api/agora/token", {
|
// await this.agoraService.handleJoinChannel(officeRoom.id, agoraToken);
|
||||||
// method: "post",
|
|
||||||
// headers: {
|
|
||||||
// "Content-Type": "application/json",
|
|
||||||
// },
|
|
||||||
// body: JSON.stringify({ channelName: "testChannel" }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
const cfResult = httpsCallable(this.functions, "agoraToken");
|
// update members list in firestore
|
||||||
|
const newMembers = [...officeRoom.members, userId];
|
||||||
return await cfResult({ channelName: "testChannel" })
|
await this.updateMembersInOfficeRoom(officeRoom.id, newMembers);
|
||||||
.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(
|
async updateMembersInOfficeRoom(
|
||||||
|
|||||||
Reference in New Issue
Block a user