|
@@ -1,294 +1,167 @@
|
|
|
<template>
|
|
|
- <Connected v-if="showConnected" :room="room" @disconnect="disconnect" />
|
|
|
-
|
|
|
- <div v-if="showAccept" class="mb-[64px] flex flex-row justify-around px-2">
|
|
|
- <div class="flex flex-col items-center" @click.stop="disconnect">
|
|
|
- <img class="h-[62px] w-[62px]" :src="hungup" alt="hungup" />
|
|
|
- <span class="mt-2 text-sm text-white">{{ $t('rtc.hungup') }}</span>
|
|
|
- </div>
|
|
|
- <div class="flex flex-col items-center" @click.stop="acceptInvitation">
|
|
|
- <img class="h-[62px] w-[62px]" :src="accept" alt="accept" />
|
|
|
- <span class="mt-2 text-sm text-white">{{ $t('rtc.accept') }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <Connected v-if="showConnected" :room="room" @disconnect="disconnect" />
|
|
|
+
|
|
|
+ <div v-if="showAccept" class="mb-[64px] flex flex-row justify-around px-2">
|
|
|
+ <div class="flex flex-col items-center" @click.stop="disconnect">
|
|
|
+ <img class="h-[62px] w-[62px]" :src="hungup" alt="hungup" />
|
|
|
+ <span class="mt-2 text-sm text-white">{{ $t("rtc.hungup") }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="flex flex-col items-center" @click.stop="acceptInvitation">
|
|
|
+ <img class="h-[62px] w-[62px]" :src="accept" alt="accept" />
|
|
|
+ <span class="mt-2 text-sm text-white">{{ $t("rtc.accept") }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
- import hungup from '@/assets/images/rtc/hungup.png'
|
|
|
- import accept from '@/assets/images/rtc/accept.png'
|
|
|
- import type { RtcInvite, WSEvent } from 'open-im-sdk-wasm/lib/types/entity'
|
|
|
- import { AuthData, InviteData, RtcInviteResults } from '../data'
|
|
|
- import { IMSDK } from '@/utils/imCommon'
|
|
|
- import useUserStore from '@/store/modules/user'
|
|
|
- import { SessionType, CbEvents } from 'open-im-sdk-wasm'
|
|
|
- import { CustomMessageType, RtcMessageStatus } from '@/constants/enum'
|
|
|
- import useMessageStore, { ExMessageItem } from '@/store/modules/message'
|
|
|
- import useConversationStore from '@/store/modules/conversation'
|
|
|
- import emitter from '@/utils/events'
|
|
|
- import { RemoteParticipant, Room, RoomEvent } from 'livekit-client'
|
|
|
- import Connected from './Connected.vue'
|
|
|
- import { getBusinessInfo } from '@/api/user'
|
|
|
-
|
|
|
- type IRtcControlEmits = {
|
|
|
- (event : 'connectRtc', data ?: AuthData) : void
|
|
|
- (event : 'closeOverlay') : void
|
|
|
- }
|
|
|
- type IRtcControlProps = {
|
|
|
- room : Room
|
|
|
- isWaiting : boolean
|
|
|
- isConnected : boolean
|
|
|
- duration : string
|
|
|
- invitation : RtcInvite
|
|
|
- inviteData : InviteData
|
|
|
- }
|
|
|
- const emit = defineEmits<IRtcControlEmits>()
|
|
|
- const props = defineProps<IRtcControlProps>()
|
|
|
-
|
|
|
- const hasJoin = ref(false)
|
|
|
- const isAccept = ref(false)
|
|
|
- const isSingle = computed(() => props.invitation.sessionType === SessionType.Single)
|
|
|
- const isRecv = computed(
|
|
|
- () => userStore.selfInfo.userID !== props.invitation?.inviterUserID,
|
|
|
- )
|
|
|
- const showAccept = computed(() => {
|
|
|
- if (props.inviteData.isJoin) {
|
|
|
- return false
|
|
|
- }
|
|
|
- if (!isRecv.value) {
|
|
|
- return false
|
|
|
- }
|
|
|
- return !props.isConnected
|
|
|
- })
|
|
|
- const showConnected = computed(() => props.isConnected || !isRecv.value)
|
|
|
-
|
|
|
- const userStore = useUserStore()
|
|
|
- const messageStore = useMessageStore()
|
|
|
- const conversationStore = useConversationStore()
|
|
|
-
|
|
|
- const insertRtcMessage = async (status : RtcMessageStatus) => {
|
|
|
- if (props.invitation.sessionType !== SessionType.Single) return
|
|
|
- const message = (
|
|
|
- await IMSDK.createCustomMessage({
|
|
|
- data: JSON.stringify({
|
|
|
- customType: CustomMessageType.Call,
|
|
|
- data: {
|
|
|
- duration: props.duration,
|
|
|
- mediaType: props.invitation.mediaType,
|
|
|
- status,
|
|
|
- },
|
|
|
- }),
|
|
|
- extension: '',
|
|
|
- description: '',
|
|
|
- })
|
|
|
- ).data
|
|
|
- if (!message) return
|
|
|
-
|
|
|
- const fullMessage = (
|
|
|
- await IMSDK.insertSingleMessageToLocalStorage<ExMessageItem>({
|
|
|
- recvID: props.invitation.inviteeUserIDList[0],
|
|
|
- sendID: props.invitation.inviterUserID,
|
|
|
- message,
|
|
|
- })
|
|
|
- ).data
|
|
|
-
|
|
|
- const userInfo = {
|
|
|
- id: '',
|
|
|
- name: '',
|
|
|
- faceUrl: '',
|
|
|
- }
|
|
|
-
|
|
|
- const getUserInfo = async () => {
|
|
|
- if (props.invitation.inviterUserID === userStore.selfInfo.userID) {
|
|
|
- const res = await getBusinessInfo(props.invitation.inviteeUserIDList[0])
|
|
|
- const businessData = res.data.users[0] ?? {}
|
|
|
- userInfo.id = props.invitation.inviteeUserIDList[0]
|
|
|
- userInfo.name = businessData.nickname
|
|
|
- userInfo.faceUrl = businessData.faceURL
|
|
|
- } else {
|
|
|
- const res = await getBusinessInfo(props.invitation.inviterUserID)
|
|
|
- const businessData = res.data.users[0] ?? {}
|
|
|
- userInfo.id = props.invitation.inviterUserID
|
|
|
- userInfo.name = businessData.nickname
|
|
|
- userInfo.faceUrl = businessData.faceURL
|
|
|
- }
|
|
|
- }
|
|
|
- await getUserInfo()
|
|
|
-
|
|
|
- insertRtcMessageToLocalStorage({ ...fullMessage, status, userInfo })
|
|
|
-
|
|
|
- const conversation = conversationStore.currentConversation
|
|
|
- if (
|
|
|
- props.invitation.inviterUserID === conversation?.userID ||
|
|
|
- props.invitation.inviteeUserIDList[0] === conversation?.userID
|
|
|
- ) {
|
|
|
- messageStore.pushNewMessage(fullMessage)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const insertRtcMessageToLocalStorage = (fullMessage : any) => {
|
|
|
- let historyList = []
|
|
|
- try {
|
|
|
- const storedHistory = localStorage.getItem(
|
|
|
- `${userStore.selfInfo.userID}_rtc_historyList`,
|
|
|
- )
|
|
|
- if (storedHistory) {
|
|
|
- historyList = JSON.parse(storedHistory)
|
|
|
- }
|
|
|
- } catch (e) { }
|
|
|
- historyList.push({
|
|
|
- ...fullMessage,
|
|
|
- time: new Date().getTime(),
|
|
|
- })
|
|
|
- localStorage.setItem(
|
|
|
- `${userStore.selfInfo.userID}_rtc_historyList`,
|
|
|
- JSON.stringify(historyList),
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- const acceptInvitation = () => {
|
|
|
- if (isAccept.value) return
|
|
|
- isAccept.value = true
|
|
|
- IMSDK.signalingAccept<RtcInviteResults>({
|
|
|
- opUserID: userStore.selfInfo.userID,
|
|
|
- invitation: props.invitation,
|
|
|
- })
|
|
|
- .then(({ data }) => {
|
|
|
- emit('connectRtc', {
|
|
|
- liveURL: data.liveURL,
|
|
|
- token: data.token,
|
|
|
- })
|
|
|
- })
|
|
|
- .catch((error) => console.log(error))
|
|
|
- }
|
|
|
-
|
|
|
- const disconnect = () => {
|
|
|
- const data = {
|
|
|
- opUserID: userStore.selfInfo.userID,
|
|
|
- invitation: props.invitation,
|
|
|
- }
|
|
|
- if (props.isWaiting) {
|
|
|
- const funcName = isRecv.value ? 'signalingReject' : 'signalingCancel'
|
|
|
- IMSDK[funcName](data)
|
|
|
- insertRtcMessage(
|
|
|
- isRecv.value ? RtcMessageStatus.Refused : RtcMessageStatus.Canceled,
|
|
|
- )
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- return
|
|
|
- }
|
|
|
- IMSDK.signalingHungUp(data)
|
|
|
- insertRtcMessage(RtcMessageStatus.Successed)
|
|
|
- props.room.disconnect()
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
-
|
|
|
- const acceptHandler = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if (props.invitation.roomID !== roomID) return
|
|
|
-
|
|
|
- if (isSingle.value) {
|
|
|
- emit('connectRtc', undefined)
|
|
|
- } else {
|
|
|
- hasJoin.value = true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const rejectHandler = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if (props.invitation.roomID !== roomID || !isSingle.value) return
|
|
|
- insertRtcMessage(RtcMessageStatus.Refused)
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
-
|
|
|
- const hangupHandler = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if ((!isSingle.value && !props.isWaiting) || props.invitation.roomID !== roomID)
|
|
|
- return
|
|
|
- insertRtcMessage(RtcMessageStatus.Successed)
|
|
|
- props.room.disconnect()
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
-
|
|
|
- const cancelHandler = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if (props.invitation.roomID !== roomID) return
|
|
|
- if (!isSingle.value && !props.isWaiting) return
|
|
|
- insertRtcMessage(RtcMessageStatus.Canceled)
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
-
|
|
|
- const timeoutHandler = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if (props.invitation.roomID !== roomID) return
|
|
|
-
|
|
|
- if (isSingle.value || !hasJoin.value) {
|
|
|
- insertRtcMessage(RtcMessageStatus.Timeout)
|
|
|
- IMSDK.signalingCancel({
|
|
|
- opUserID: userStore.selfInfo.userID,
|
|
|
- invitation: props.invitation,
|
|
|
- })
|
|
|
- if (!isSingle.value) props.room.disconnect()
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const handleByOtherDevice = ({
|
|
|
- data: {
|
|
|
- invitation: { roomID },
|
|
|
- },
|
|
|
- } : WSEvent<{ invitation : RtcInvite }>) => {
|
|
|
- if (props.invitation.roomID !== roomID) return
|
|
|
- insertRtcMessage(RtcMessageStatus.HandleByOtherDevice)
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
-
|
|
|
- const participantDisconnectedHandler = (remoteParticipant : RemoteParticipant) => {
|
|
|
- if (!isSingle.value) return
|
|
|
-
|
|
|
- const identity = remoteParticipant.identity
|
|
|
- if (
|
|
|
- identity === props.invitation.inviterUserID ||
|
|
|
- identity === props.invitation.inviteeUserIDList[0]
|
|
|
- ) {
|
|
|
- insertRtcMessage(RtcMessageStatus.Successed)
|
|
|
- props.room.disconnect()
|
|
|
- emitter.emit('CLOSE_RTC_MODAL')
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- onMounted(() => {
|
|
|
- IMSDK.on(CbEvents.OnInviteeAccepted, acceptHandler)
|
|
|
- IMSDK.on(CbEvents.OnInviteeRejected, rejectHandler)
|
|
|
- IMSDK.on(CbEvents.OnHangUp, hangupHandler)
|
|
|
- IMSDK.on(CbEvents.OnInvitationCancelled, cancelHandler)
|
|
|
- IMSDK.on(CbEvents.OnInvitationTimeout, timeoutHandler)
|
|
|
- IMSDK.on(CbEvents.OnInviteeAcceptedByOtherDevice, handleByOtherDevice)
|
|
|
- IMSDK.on(CbEvents.OnInviteeRejectedByOtherDevice, handleByOtherDevice)
|
|
|
- props.room.on(RoomEvent.ParticipantDisconnected, participantDisconnectedHandler)
|
|
|
- })
|
|
|
-
|
|
|
- onUnmounted(() => {
|
|
|
- IMSDK.off(CbEvents.OnInviteeAccepted, acceptHandler)
|
|
|
- IMSDK.off(CbEvents.OnInviteeRejected, rejectHandler)
|
|
|
- IMSDK.off(CbEvents.OnHangUp, hangupHandler)
|
|
|
- IMSDK.off(CbEvents.OnInvitationCancelled, cancelHandler)
|
|
|
- IMSDK.off(CbEvents.OnInvitationTimeout, timeoutHandler)
|
|
|
- IMSDK.off(CbEvents.OnInviteeAcceptedByOtherDevice, handleByOtherDevice)
|
|
|
- IMSDK.off(CbEvents.OnInviteeRejectedByOtherDevice, handleByOtherDevice)
|
|
|
- props.room.off(RoomEvent.ParticipantDisconnected, participantDisconnectedHandler)
|
|
|
- })
|
|
|
-</script>
|
|
|
+import hungup from "@/assets/images/rtc/hungup.png";
|
|
|
+import accept from "@/assets/images/rtc/accept.png";
|
|
|
+import type { RtcInvite, WSEvent } from 'open-im-sdk-wasm/lib/types/entity'
|
|
|
+import { AuthData, InviteData } from "../data";
|
|
|
+import { IMSDK } from "@/utils/imCommon";
|
|
|
+import useUserStore from "@/store/modules/user";
|
|
|
+import { SessionType, CbEvents, MessageType } from "open-im-sdk-wasm";
|
|
|
+import { CustomType } from "@/constants/enum";
|
|
|
+import { ExMessageItem } from "@/store/modules/message";
|
|
|
+import emitter from "@/utils/events";
|
|
|
+import { RemoteParticipant, Room, RoomEvent } from "livekit-client";
|
|
|
+import Connected from "./Connected.vue";
|
|
|
+import { getRtcConnectData } from "@/api/im";
|
|
|
+import { v4 as uuidV4 } from "uuid";
|
|
|
+
|
|
|
+type IRtcControlEmits = {
|
|
|
+ (event: "connectRtc", data?: AuthData): void;
|
|
|
+ (event: "closeOverlay"): void;
|
|
|
+};
|
|
|
+type IRtcControlProps = {
|
|
|
+ room: Room;
|
|
|
+ isWaiting: boolean;
|
|
|
+ isConnected: boolean;
|
|
|
+ duration: string;
|
|
|
+ invitation: RtcInvite;
|
|
|
+ inviteData: InviteData;
|
|
|
+ sendCustomSignal: (recvID: string, customType: CustomType) => Promise<void>;
|
|
|
+};
|
|
|
+const emit = defineEmits<IRtcControlEmits>();
|
|
|
+const props = defineProps<IRtcControlProps>();
|
|
|
+
|
|
|
+const userStore = useUserStore();
|
|
|
+
|
|
|
+const isRecv = computed(
|
|
|
+ () => userStore.selfInfo.userID !== props.invitation?.inviterUserID
|
|
|
+);
|
|
|
+const recvID = isRecv.value
|
|
|
+ ? props.invitation.inviterUserID
|
|
|
+ : props.invitation.inviteeUserIDList[0];
|
|
|
+const showAccept = computed(() => {
|
|
|
+ if (!isRecv.value) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return !props.isConnected;
|
|
|
+});
|
|
|
+const showConnected = computed(() => props.isConnected || !isRecv.value);
|
|
|
+
|
|
|
+const acceptInvitation = async () => {
|
|
|
+ try {
|
|
|
+ await props.sendCustomSignal(recvID, CustomType.CallingAccept);
|
|
|
+ const { data } = await getRtcConnectData(
|
|
|
+ `room-${recvID}`, // 房间号
|
|
|
+ userStore.selfInfo.userID
|
|
|
+ );
|
|
|
+ emit("connectRtc", {
|
|
|
+ liveURL: data.serverUrl,
|
|
|
+ token: data.token,
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const disconnect = () => {
|
|
|
+ if (props.isWaiting) {
|
|
|
+ const customType = isRecv
|
|
|
+ ? CustomType.CallingReject
|
|
|
+ : CustomType.CallingCancel;
|
|
|
+ props.sendCustomSignal(recvID, customType);
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ props.sendCustomSignal(recvID, CustomType.CallingHungup);
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+};
|
|
|
+
|
|
|
+const acceptHandler = async ({ roomID }: RtcInvite) => {
|
|
|
+ if (props.invitation.roomID !== roomID) return;
|
|
|
+ emit("connectRtc", undefined);
|
|
|
+};
|
|
|
+
|
|
|
+const rejectHandler = ({ roomID }: RtcInvite) => {
|
|
|
+ if (props.invitation.roomID !== roomID) return;
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+};
|
|
|
+
|
|
|
+const hangupHandler = ({ roomID }: RtcInvite) => {
|
|
|
+ if (props.invitation.roomID !== roomID) return;
|
|
|
+ props.room.disconnect();
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+};
|
|
|
+
|
|
|
+const cancelHandler = ({ roomID }: RtcInvite) => {
|
|
|
+ if (props.invitation.roomID !== roomID) return;
|
|
|
+ if (!props.isWaiting) return;
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+};
|
|
|
+
|
|
|
+const participantDisconnectedHandler = (
|
|
|
+ remoteParticipant: RemoteParticipant
|
|
|
+) => {
|
|
|
+ const identity = remoteParticipant.identity;
|
|
|
+ if (
|
|
|
+ identity === props.invitation.inviterUserID ||
|
|
|
+ identity === props.invitation.inviteeUserIDList[0]
|
|
|
+ ) {
|
|
|
+ props.room.disconnect();
|
|
|
+ emitter.emit("CLOSE_RTC_MODAL");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const newMessageHandler = ({ data }: WSEvent<ExMessageItem[]>) => {
|
|
|
+ data.map((message) => {
|
|
|
+ if (message.contentType === MessageType.CustomMessage) {
|
|
|
+ const customData = JSON.parse(message.customElem!.data) as {
|
|
|
+ data: RtcInvite;
|
|
|
+ customType: CustomType;
|
|
|
+ };
|
|
|
+ if (customData.customType === CustomType.CallingAccept) {
|
|
|
+ acceptHandler(customData.data);
|
|
|
+ }
|
|
|
+ if (customData.customType === CustomType.CallingReject) {
|
|
|
+ rejectHandler(customData.data);
|
|
|
+ }
|
|
|
+ if (customData.customType === CustomType.CallingCancel) {
|
|
|
+ cancelHandler(customData.data);
|
|
|
+ }
|
|
|
+ if (customData.customType === CustomType.CallingHungup) {
|
|
|
+ hangupHandler(customData.data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ IMSDK.on(CbEvents.OnRecvNewMessages, newMessageHandler);
|
|
|
+ props.room.on(
|
|
|
+ RoomEvent.ParticipantDisconnected,
|
|
|
+ participantDisconnectedHandler
|
|
|
+ );
|
|
|
+});
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ IMSDK.off(CbEvents.OnRecvNewMessages, newMessageHandler);
|
|
|
+ props.room.off(
|
|
|
+ RoomEvent.ParticipantDisconnected,
|
|
|
+ participantDisconnectedHandler
|
|
|
+ );
|
|
|
+});
|
|
|
+</script>
|