diff --git a/code/client/certs/cert.pem b/code/client/certs/cert.pem new file mode 100644 index 0000000..ff95dfc --- /dev/null +++ b/code/client/certs/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIELjCCApagAwIBAgIQXasrLEwSwhmrN7pzzSHxyjANBgkqhkiG9w0BAQsFADBh +MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGzAZBgNVBAsMEnN1bnhp +bkBNYWNCb29rLVBybzEiMCAGA1UEAwwZbWtjZXJ0IHN1bnhpbkBNYWNCb29rLVBy +bzAeFw0yMzA0MjgxMzE2NDRaFw0yNTA3MjgxMzE2NDRaME4xJzAlBgNVBAoTHm1r +Y2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEjMCEGA1UECwwac3VueGluQE1h +Y0Jvb2stUHJvLTMubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDsmvFnOJSxXoafj/ygI/AQnZC6kdSr4ma9sCI93QXYJS+uaXWlpAjweDjLmNBY +33oYeqkcI5nShPzKlX4HfiE+eW336z4dZaLx/d0zrwGc1M3+Go5bRQ5NsZFdZ/sW +CuB0WQ74BRW3Se2vBWISOtrWZHHIYUJ3fGRlJIeZBnVyAYGKHlswuWxgclZWbb2E +wLKSThMZWI9gP0/iSmffzhDYW8EyRwpQNzUQQPe/5KbICvYdXvu2f/uRALufUqfR +truFmK4/IWr0CMVWgfWyUGT0/TCWE81lZsh1a6n2NI0NGsux7ipOKsmpmcZwRMmZ +A4tSItWcorOXSI1vKn/zRn1lAgMBAAGjdTBzMA4GA1UdDwEB/wQEAwIFoDATBgNV +HSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBRoTUPg3+4+ht0WwhN/HmZHU2+W +pzArBgNVHREEJDAigg50ZWFtbGlua2VyLmNvbYIQKi50ZWFtbGlua2VyLmNvbTAN +BgkqhkiG9w0BAQsFAAOCAYEAFWmRN9VvuPkIpTe6RTumc4QZve2KjnZVi6FMXpKw +ywUhBQqxd0XlTFdFmXUDEFcPIR9zoDD9l8Y37ZuAd0Cb0k4r0bSV4IWWa7gc8ihN +PwVQqrcnwbTFT5udBl09oSKH9BNrw2yiv1jOgz2NpWm0HwOYBxBOlLwoVaCqvFg4 +bNp3xutXcUqoS3TP5S2wnArBMJ2+ZvtzO1YjnfpF3jZZxh27ewRxzYcSxrrWkCyc +Xkrt4im5PQJ188+pke+wuNFPSZij5NZYr2UZmRsHA7sR2jIiRRBUNeYxuEMpWNUq +YQfpLPgk189yP8xMDZhUyMgQ0eOH12nXDoMamESCgPknACFJ0wffuoQqpVvtEyB/ +4KOzYvMZkW/apdmzI1LCSwyBJH8QO9m8la2960CiQ02upN/lRTG1Ylo09N+leFQ9 +x3mLrfjwK0t+McX4d4lD9bPPQbzjujYJK1ra8MEagvEdZMCMaenAyDqK8D3v0DO+ +NdELh+MOxvuEcs1LvFi58yuJ +-----END CERTIFICATE----- diff --git a/code/client/certs/key.pem b/code/client/certs/key.pem new file mode 100644 index 0000000..d83b2c7 --- /dev/null +++ b/code/client/certs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDsmvFnOJSxXoaf +j/ygI/AQnZC6kdSr4ma9sCI93QXYJS+uaXWlpAjweDjLmNBY33oYeqkcI5nShPzK +lX4HfiE+eW336z4dZaLx/d0zrwGc1M3+Go5bRQ5NsZFdZ/sWCuB0WQ74BRW3Se2v +BWISOtrWZHHIYUJ3fGRlJIeZBnVyAYGKHlswuWxgclZWbb2EwLKSThMZWI9gP0/i +SmffzhDYW8EyRwpQNzUQQPe/5KbICvYdXvu2f/uRALufUqfRtruFmK4/IWr0CMVW +gfWyUGT0/TCWE81lZsh1a6n2NI0NGsux7ipOKsmpmcZwRMmZA4tSItWcorOXSI1v +Kn/zRn1lAgMBAAECggEBAIzJFWNqE3AG4uwG7TcMq0f3uaqKI5WzPZcZOwa8gUG/ +vsN1FP/ev0L3BjR/VVnMgAYY1o9bz6eoYhLZKQikUHuwHXMrkuZDF9YOTJT4SWlc +ZsYQXyyHxp8MTYba3FidWDli1LlXrThG0RsXhOd8BcMUOXAWD0qxxbs5JUY0xaVz +gwew95LUB12vBpMb3DMjLuUOFpCxVLtThsLYb+TMVzYm706qavmnbHs8xgFeOzUh +lHyIBdoLhrg/aeD0rWQVepyq2j8YP2N9N2sU8SNzsPhtsR5bZAU5DuRlrVy+ZTuL +IOFlk4aq4bjzCe7KxvHb+82DnaT7O4cyf7qvL/LYe/ECgYEA+iKWiqYSbICAN9Vn +58uwZFX5VEANCmAhVufZe4I4MrMWqhJjbpiJBmnoVXXfHHyLvHO52/FKu1xE6TUB +mqD7H32nWuXw+3wuscE01furXIfrVhZjhx86nWOjub932N4L/OgKxlkfKWVR4oM7 +AJuzwsVMkTz2pg1bvk8AVc6Cjg8CgYEA8ickqkHWpTeVPS/XBPg4jCcvnhyZBu0a +kkokizmb4Yr7yqXF30gdsMx3WVPJ2XhlFjmsQD1BbhUGYkKfUIOCS2APAWg+3JOT +1y9wmTY0uE8JwWvH5GBLEw8gBqz7w+Z5rfYXqpD7gBm1tpc4BBg1QDP64Ldsm4g0 +c3KFNsLKMUsCgYEA7TsYo+7V0modMNcJcOHSLZcMnUcSFyEM/aturKDYQ91uRWaj +PyUT8C1J2KOuMwo8TUNEpsC2K/RatwM6vjinczptGtyyLRGeB6BCSCAkaeHO5Rre +0ixgHx19DpH1TI1ruTUp4uxrjYs2min0L6N8XeFZuPWkx+ArftbWbospbykCgYA+ +ESZmtWVtB6gq/L4iOfVUhx6/ahkXG2L6KCLhxKxdzR/ou0DSkEt764yTytQr954B +alrqREknDCCMwwLOwkZ9R2vRMoUaSIFWYIR94NT2gNvBRf0AXmYfxnqw+1m0xrhd +jHxYbbzpAq3+1Re4hPPxNuGRA7LE0s6O4MWgWaE1rQKBgQD0tCVevGWS3A/cE/ye +a+Mu7IakKTBqC0tNzsG6VuzqjcNrR4+h0nJDJjMsOL/pKAzfI7WnruFOA2xMvI4W +36ij9rm2r3zG7K19q04eF0bz+kPTFefJHXYwmeQkEbryeibzlFc458U9aSWUHXof +QVhit6C7sVVp7so48x/eBdiVsg== +-----END PRIVATE KEY----- diff --git a/code/client/package.json b/code/client/package.json index cf459f4..c9cce57 100644 --- a/code/client/package.json +++ b/code/client/package.json @@ -10,27 +10,30 @@ }, "dependencies": { "@logicflow/core": "^1.1.31", + "blueimp-md5": "^2.19.0", "eventemitter3": "^5.0.0", + "mediasoup-client": "^3.6.84", "moment": "^2.29.4", "moment-timezone": "^0.5.42", "pinia": "^2.0.28", "socket.io-client": "^4.6.1", "uuid": "^9.0.0", - "vue": "^3.2.47", + "vue": "^3.3.4", "vue-router": "^4.1.6" }, "devDependencies": { - "@arco-design/web-vue": "^2.45.1", + "@arco-design/web-vue": "^2.46.0", "@rollup/plugin-typescript": "^10.0.1", + "@types/blueimp-md5": "^2.18.0", "@types/node": "^18.15.11", "@types/uuid": "^9.0.0", - "@vitejs/plugin-vue": "^4.0.0", + "@vitejs/plugin-vue": "^4.2.0", "rollup-plugin-typescript2": "^0.34.1", "tslib": "^2.4.1", "ttypescript": "^1.5.15", "typescript": "^4.6.4", - "vite": "^4.0.4", + "vite": "^4.3.2", "vite-plugin-typescript": "^1.0.4", - "vue-tsc": "^1.0.12" + "vue-tsc": "^1.4.4" } } diff --git a/code/client/src/assert/index_sample.png b/code/client/src/assert/index_sample.png new file mode 100644 index 0000000..302958c Binary files /dev/null and b/code/client/src/assert/index_sample.png differ diff --git a/code/client/src/business/common/component/chat/chat.vue b/code/client/src/business/common/component/chat/chat.vue index 931b3b3..6dceafa 100644 --- a/code/client/src/business/common/component/chat/chat.vue +++ b/code/client/src/business/common/component/chat/chat.vue @@ -2,9 +2,9 @@
-
- -
+ + {{calculateShortName(info.name)}} +   {{ info.name }}
@@ -29,7 +29,9 @@
- + + {{calculateShortName(item.name)}} +
@@ -42,7 +44,10 @@
- {{item.content}} + +
@@ -50,21 +55,24 @@
- + @@ -76,8 +84,11 @@ import {IClient_Chat_Message_Item} from "./type"; import {nextTick, ref, watch} from "vue"; import {ECommon_IM_Message_EntityType} from "../../../../../../common/model/im_unread_message"; import {ECommon_IM_Message_ContentType} from "../../../../../../common/model/im_user_message"; -import moment from "moment"; import {SessionStorage} from "../../storage/session"; +import Image from "./operation/image.vue" +import moment from "moment"; +import ImageMessage from "./messageType/imageMessage.vue"; +import {calculateShortName} from "../../util/helper"; const root=ref() const emit=defineEmits<{ @@ -98,11 +109,14 @@ const props = defineProps<{ id: string } }>() +const loading=ref(false) const contentRef=ref() const content=ref("") const isInfoVisible=ref(false) const messageList = ref([]) const organizationUserId=SessionStorage.get("organizationUserId") +const imageRef=ref>() +let isWheel=false let preId="" watch(() => props.data, () => { messageList.value = JSON.parse(JSON.stringify(props.data)) @@ -123,7 +137,12 @@ watch(() => props.data, () => { deep:true, immediate: true }) +watch(()=>props.info,()=>{ + isWheel=false + isInfoVisible.value=false +}) const onWheel=(event:WheelEvent)=>{ + isWheel=true; let ele=event.currentTarget as HTMLDivElement if(event.deltaY<0 && ele.scrollTop<=0) { emit("updateMore",messageList.value[0],props.type,props.info) @@ -142,6 +161,30 @@ const onSend=()=>{ const scrollToBottom=()=>{ contentRef.value.scrollTop=contentRef.value.scrollHeight } + +const onUpdateImage=(fileId:string)=>{ + loading.value=false + emit("send",fileId,ECommon_IM_Message_ContentType.IMAGE,props.type,props.type===ECommon_IM_Message_EntityType.USER?props.info.id:null,props.type===ECommon_IM_Message_EntityType.TEAM?props.info.id:null) +} +const onPaste=async (event:ClipboardEvent)=>{ + let items=event.clipboardData.items + for(let i=0;i{ + if(!isWheel) { + contentRef.value.scrollTop=contentRef.value.scrollHeight + } +} + defineExpose({ scrollToBottom }) diff --git a/code/client/src/business/common/component/chat/messageType/imageMessage.vue b/code/client/src/business/common/component/chat/messageType/imageMessage.vue new file mode 100644 index 0000000..67cafa0 --- /dev/null +++ b/code/client/src/business/common/component/chat/messageType/imageMessage.vue @@ -0,0 +1,41 @@ + + + + + \ No newline at end of file diff --git a/code/client/src/business/common/component/chat/operation/image.vue b/code/client/src/business/common/component/chat/operation/image.vue new file mode 100644 index 0000000..fb94138 --- /dev/null +++ b/code/client/src/business/common/component/chat/operation/image.vue @@ -0,0 +1,76 @@ + + + + + \ No newline at end of file diff --git a/code/client/src/business/common/component/meeting/AutoExecuteArray.ts b/code/client/src/business/common/component/meeting/AutoExecuteArray.ts new file mode 100644 index 0000000..410e8fa --- /dev/null +++ b/code/client/src/business/common/component/meeting/AutoExecuteArray.ts @@ -0,0 +1,15 @@ +export class AutoExecuteArray { + private static arr=[] + static async push(func:any) { + if(this.arr.length>0) { + this.arr.unshift(func) + } else { + this.arr.unshift(func) + while(this.arr.length>0) { + let func=this.arr[this.arr.length-1] + await func() + this.arr.pop() + } + } + } +} \ No newline at end of file diff --git a/code/client/src/business/common/component/meeting/client.ts b/code/client/src/business/common/component/meeting/client.ts new file mode 100644 index 0000000..599154c --- /dev/null +++ b/code/client/src/business/common/component/meeting/client.ts @@ -0,0 +1,336 @@ +import * as mediaSoup from "mediasoup-client"; +import {Device} from "mediasoup-client"; +import {Transport} from "mediasoup-client/lib/Transport"; +import {Socket} from "socket.io-client"; +import {Meeting_ClientToServerEvents, Meeting_ServerToClientEvents} from "./type"; +import {AutoExecuteArray} from "./AutoExecuteArray"; +import {MediaKind, RtpCapabilities} from "mediasoup-client/lib/RtpParameters"; + +export class MeetingClient { + private device:Device + private producerAudio:mediaSoup.types.Producer + private producerVideo:mediaSoup.types.Producer + private producerSet=new Set() + private transportReceive:Transport + private transportSend:Transport + private socket:Socket + private roomInfo:{ + roomId:string, + roomName:string + } + onProducerStateChange:(state:"new"|"close"|"pause"|"resume", kind: mediaSoup.types.MediaKind, businessId:string,stream?: MediaStream,producerId?:string)=>void + onLocalProducerInit:(stream:MediaStream)=>void + onLocalProducerStart:(kind:MediaKind)=>void + onJoinedRoom:(roomInfo:typeof this.roomInfo)=>void + onLeavedRoom:(roomInfo:typeof this.roomInfo)=>void + onSpeaker:(businessId:string)=>void + onKick:()=>void + private onDisconnect:any + constructor(socket:any) { + this.onDisconnect=this._onDisconnect.bind(this) + this.socket=socket + this.socket.on('newProducer', async ( producerId) => { + if(!this.producerSet.has(producerId)) { + this.producerSet.add(producerId) + AutoExecuteArray.push(this.subscribe.bind(this,producerId)) + } + }); + + this.socket.on('producerClosed', (producerId, kind, businessId)=>{ + if(this.onProducerStateChange) { + this.onProducerStateChange("close",kind,businessId,null,producerId) + } + this.producerSet.delete(producerId) + }); + + this.socket.on("producerPause",(producerId, kind, businessId) => { + if(this.onProducerStateChange) { + this.onProducerStateChange("pause",kind,businessId,null,producerId) + } + }) + + this.socket.on("producerResume",(producerId, kind, businessId) => { + if(this.onProducerStateChange) { + this.onProducerStateChange("resume",kind,businessId,null,producerId) + } + }) + + this.socket.on("kick",() => { + this.clearRoomConnection() + if(this.onKick) { + this.onKick() + } + }) + + this.socket.on("disconnect",this.onDisconnect) + + this.socket.on("speaker",businessId => { + if(this.onSpeaker) { + this.onSpeaker(businessId) + } + }) + } + private _onDisconnect(reason) { + this.clearRoomConnection() + } + getRoomInfo() { + return this.roomInfo + } + async join(roomId:string,extraData:any):Promise<{ + success:boolean, + msg?:string + }> { + if(this.roomInfo) { + return { + success:false, + msg:"you have joined a meeting" + } + } + let ret=await this.socket.emitWithAck("joinRoom",roomId,extraData) + if(ret) { + this.roomInfo=ret; + } + if(!this.device) { + const data = await this.socket.emitWithAck('getRouterRtpCapabilities'); + await this.loadDevice(data); + } + await this.publish() + return { + success:ret?true:false + } + } + async leave():Promise { + if(!this.roomInfo) { + return false + } + await this.socket.emitWithAck("leaveRoom") + this.clearRoomConnection() + return true + } + async pause(kind:MediaKind) { + let ret=await this.socket.emitWithAck("pauseSelf",kind) + return ret; + } + + async resume(kind:MediaKind) { + let ret=await this.socket.emitWithAck("resumeSelf",kind) + return ret; + } + async mute(kind:MediaKind,businessId:string) { + let ret=await this.socket.emitWithAck("pauseOther",kind,businessId) + return ret; + } + async unmute(kind:MediaKind,businessId:string) { + let ret=await this.socket.emitWithAck("resumeOther",kind,businessId) + return ret; + } + async kick(businessId:string) { + let ret=await this.socket.emitWithAck("kick",businessId) + return ret; + } + async end() { + let ret=await this.socket.emitWithAck("end") + return ret; + } + async states() { + let ret=await this.socket.emitWithAck("states") + return ret; + } + private clearRoomConnection() { + if(this.roomInfo) { + if(this.onLeavedRoom) { + this.onLeavedRoom(Object.assign({},this.roomInfo)) + } + this.roomInfo=null; + } + this.device=null; + this.producerSet=new Set + if(this.producerAudio) { + this.producerAudio.removeAllListeners(); + this.producerAudio.close() + this.producerAudio=null; + } + if(this.producerVideo) { + this.producerVideo.removeAllListeners() + this.producerVideo.close() + this.producerVideo=null; + } + if(this.transportReceive) { + this.transportReceive.removeAllListeners() + this.transportReceive.close() + this.transportReceive=null + } + if(this.transportSend) { + this.transportSend.removeAllListeners() + this.transportSend.close() + this.transportSend=null + } + this.socket.removeAllListeners("newProducer") + this.socket.removeAllListeners("producerClosed") + this.socket.removeAllListeners("producerPause") + this.socket.removeAllListeners("producerResume") + this.socket.removeAllListeners("kick") + this.socket.removeAllListeners("speaker") + this.socket.off("disconnect",this.onDisconnect) + this.socket=null; + } + private async loadDevice(routerRtpCapabilities:RtpCapabilities) { + try { + this.device = new mediaSoup.Device(); + } catch (error) { + if (error.name === 'UnsupportedError') { + console.error('browser not supported'); + } + } + await this.device.load({ routerRtpCapabilities }); + } + + private async subscribe(remoteProducerId:string) { + if(!this.transportReceive) { + const data = await this.socket.emitWithAck('createConsumerTransport'); + if (!data) { + this.producerSet.delete(remoteProducerId) + return; + } + this.transportReceive = this.device.createRecvTransport({...data, iceServers : []}); + this.transportReceive.on('connect', async ({ dtlsParameters }, callback, errback) => { + this.socket.emitWithAck('connectConsumerTransport', { + dtlsParameters + }) + .then(callback) + .catch(errback); + }); + this.transportReceive.on('connectionstatechange', async (state) => { + switch (state) { + case 'connecting': + console.log("Connecting to consumer for audio, transport id: " + this.transportReceive.id) + break; + case 'connected': + console.log("Connected to consumer for audio, transport id: " + this.transportReceive.id) + break; + case 'failed': + case "closed": + case "disconnected": { + this.leave() + break + } + default: break; + } + }); + } + this.consume(this.transportReceive, remoteProducerId).then(async value=>{ + await this.socket.emitWithAck("resume",value.consumer.id) + if(this.onProducerStateChange) { + this.onProducerStateChange("new",value.consumer.kind,value.businessId,value.stream,remoteProducerId) + } + }) + } + + private async consume(transport: mediaSoup.types.Transport, remoteProducerId:string) { + const { rtpCapabilities } = this.device; + const transportId = transport.id; + const data = await this.socket.emitWithAck('consume', { rtpCapabilities, remoteProducerId, transportId}); + const { + producerId, + id, + kind, + rtpParameters, + } = data; + const consumer = await transport.consume({ + id, + producerId, + kind, + rtpParameters, + }); + const stream = new MediaStream(); + stream.addTrack(consumer.track); + return {stream,consumer,businessId:data.businessId}; + } + + private async publish() { + const data = await this.socket.emitWithAck('createProducerTransport'); + if (!data) { + return; + } + this.transportSend = this.device.createSendTransport({...data, iceServers : []}); + this.transportSend.on('connect', async ({ dtlsParameters }, callback, errback) => { + this.socket.emitWithAck('connectProducerTransport', { dtlsParameters }) + .then(callback) + .catch(errback); + }); + this.transportSend.on('produce', async ({ kind, rtpParameters }, callback, errback) => { + try { + const { id, producersExist } = await this.socket.emitWithAck('produce', { + kind, + rtpParameters, + }); + if(this.onLocalProducerStart) { + this.onLocalProducerStart(kind) + } + if (producersExist){ + this.getProducers() + } + callback({ id }); + } catch (err) { + errback(err); + } + }); + this.transportSend.on('connectionstatechange', (state) => { + switch (state) { + case 'connecting': + console.log("Connecting to publish") + break; + case 'connected': + console.log("Connected") + if(this.onJoinedRoom) { + this.onJoinedRoom(this.roomInfo) + } + break; + case 'failed': + this.transportSend.close(); + console.log("Failed connection") + break; + default: break; + } + }); + const mediaConstraints:MediaStreamConstraints = { + audio: { + echoCancellation:true, + noiseSuppression:true + }, + video: true, + } + navigator.mediaDevices.getUserMedia(mediaConstraints).then( async (stream) => { + if(this.onLocalProducerInit) { + this.onLocalProducerInit(stream) + } + let track = stream.getAudioTracks()[0]; + if(track) { + let params:any = { track }; + params.codecOptions = { + opusStereo: 1, + opusDtx: 1 + } + this.producerAudio =await this.transportSend.produce(params); + } + track=stream.getVideoTracks()[0] + if(track) { + let params={track} + this.producerVideo=await this.transportSend.produce(params) + } + }).catch(reason => { + console.log(reason) + }) + } + + private getProducers(){ + this.socket.emit('getProducers', async producerIds => { + for(let id of producerIds) { + if(!this.producerSet.has(id)) { + this.producerSet.add(id) + AutoExecuteArray.push(this.subscribe.bind(this,id)) + } + } + }) + } +} \ No newline at end of file diff --git a/code/client/src/business/common/component/meeting/type.ts b/code/client/src/business/common/component/meeting/type.ts new file mode 100644 index 0000000..e64ca87 --- /dev/null +++ b/code/client/src/business/common/component/meeting/type.ts @@ -0,0 +1,84 @@ +import type {MediaKind, RtpCapabilities, RtpParameters} from "mediasoup-client/lib/RtpParameters"; +import {DtlsParameters, IceCandidate, IceParameters} from "mediasoup-client/lib/Transport"; + +import {SctpParameters} from "mediasoup-client/lib/SctpParameters"; + +export interface Meeting_ServerToClientEvents { + newProducer:(producerId:string,kind:MediaKind,businessId:string)=>void + producerClosed:(producerId:string,kind:MediaKind,businessId:string)=>void + producerPause:(producerId:string,kind:MediaKind,businessId:string)=>void + producerResume:(producerId:string,kind:MediaKind,businessId:string)=>void + kick:()=>void + speaker:(businessId:string)=>void +} + +export interface Meeting_ClientToServerEvents { + joinRoom:(roomId:string,extraData:any,callback:(info:{ + roomId:string, + roomName:string + },msg?:string)=>void)=>void + leaveRoom:(callback:()=>void)=>void + getRouterRtpCapabilities:(callback:(capabilities:RtpCapabilities)=>void)=>void + createProducerTransport:(callback:(param:{ + id: string, + iceParameters: IceParameters, + iceCandidates: IceCandidate[], + dtlsParameters: DtlsParameters, + sctpParameters: SctpParameters, + })=>void)=>void + createConsumerTransport:(callback:(param:{ + id: string, + iceParameters: IceParameters, + iceCandidates: IceCandidate[], + dtlsParameters: DtlsParameters, + sctpParameters: SctpParameters, + })=>void)=>void + connectProducerTransport:(data:{ + dtlsParameters:DtlsParameters + },callback:()=>void)=>void + connectConsumerTransport:(data:{ + dtlsParameters:DtlsParameters + },callback:()=>void)=>void + produce:(data:{ + kind:MediaKind, + rtpParameters:RtpParameters + },callback:(producerInfo:{ + id:string, + producersExist:boolean + })=>void)=>void + consume:(data:{ + rtpCapabilities:RtpCapabilities, + remoteProducerId:string, + transportId:string + },callback:(data:{ + businessId:string, + producerId: string, + id: string, + kind: MediaKind, + rtpParameters: RtpParameters, + type: 'simple' | 'simulcast' | 'svc' | 'pipe', + producerPaused: boolean + })=>void)=>void + getProducers:(callback:(producerList:string[])=>void)=>void + resume:(consumerId:string,callback:()=>void)=>void + pauseSelf:(kind:MediaKind,callback:()=>void)=>void + resumeSelf:(kind:MediaKind,callback:()=>void)=>void + pauseOther:(kind:MediaKind,businessId:string,callback:(success:boolean)=>void)=>void + resumeOther:(kind:MediaKind,businessId:string,callback:(success:boolean)=>void)=>void + kick:(businessId:string,callback:(success:boolean)=>void)=>void + end:(callback:(success:boolean)=>void)=>void + states:(callback:(list:{ + businessId:string, + kinds:{ + [kind:string]:boolean + } + }[])=>void)=>void +} + +export interface Meeting_InterServerEvents { + +} + +export interface Meeting_Data { + +} diff --git a/code/client/src/business/common/component/notification/notification.ts b/code/client/src/business/common/component/notification/notification.ts index b27abb8..f9ea053 100644 --- a/code/client/src/business/common/component/notification/notification.ts +++ b/code/client/src/business/common/component/notification/notification.ts @@ -3,7 +3,8 @@ import {h} from "vue"; export enum NotificationType { CALENDAR="calendar", - IM="im" + IM="im", + MEETING="meeting" } export type NotificationCallbackFunc=(type:NotificationType,data:any)=>void export class NotificationWrapper { diff --git a/code/client/src/business/common/component/userAvatar.vue b/code/client/src/business/common/component/userAvatar.vue index ee41e69..efc1752 100644 --- a/code/client/src/business/common/component/userAvatar.vue +++ b/code/client/src/business/common/component/userAvatar.vue @@ -1,20 +1,33 @@