From df0c8afe38c058e67866eab63dfd02154df922be Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 22:50:12 +0900 Subject: [PATCH 01/63] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/ticle/ticle.controller.ts | 8 +++++++- apps/api/src/ticle/ticle.service.ts | 17 +++++++++++++++++ packages/types/src/errorMessages.ts | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/api/src/ticle/ticle.controller.ts b/apps/api/src/ticle/ticle.controller.ts index f2085ed8..fc6ca23e 100644 --- a/apps/api/src/ticle/ticle.controller.ts +++ b/apps/api/src/ticle/ticle.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Query, UseGuards } from '@nestjs/common'; import { CreateTicleSchema } from '@repo/types'; import { JwtAuthGuard } from '@/auth/jwt/jwt-auth.guard'; @@ -50,4 +50,10 @@ export class TicleController { applyToTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { return this.ticleService.applyTicle(ticleId, userId); } + + @Delete(':ticleId') + @UseGuards(JwtAuthGuard) + deleteTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { + return this.ticleService.deleteTicle(ticleId, userId); + } } diff --git a/apps/api/src/ticle/ticle.service.ts b/apps/api/src/ticle/ticle.service.ts index a2ec54c6..f3bfd345 100644 --- a/apps/api/src/ticle/ticle.service.ts +++ b/apps/api/src/ticle/ticle.service.ts @@ -226,4 +226,21 @@ export class TicleService { }, }; } + + async deleteTicle(userId: number, ticleId: number) { + const ticle = await this.ticleRepository.findOne({ + where: { id: ticleId, speaker: { id: userId } }, + }); + + if (ticle.speaker.id === userId) { + throw new BadRequestException(ErrorMessage.CANNOT_DELETE_OTHERS_TICLE); + } + + if (!ticle) { + throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); + } + + await this.ticleRepository.remove(ticle); + return; + } } diff --git a/packages/types/src/errorMessages.ts b/packages/types/src/errorMessages.ts index b4aa65cd..911793dc 100644 --- a/packages/types/src/errorMessages.ts +++ b/packages/types/src/errorMessages.ts @@ -14,6 +14,7 @@ export const ErrorMessage = { FAILED_TO_CREATE_TICLE: '티클 생성에 실패했습니다', CANNOT_REQUEST_OWN_TICLE: '자신이 발표자인 티클에는 신청할 수 없습니다', TICLE_ALREADY_REQUESTED: '이미 신청한 티클입니다', + CANNOT_DELETE_OTHERS_TICLE: '다른 사람의 티클은 삭제할 수 없습니다', // socket error PEER_NOT_FOUND_IN_ROOM: '방에 해당 peer가 존재하지 않습니다', From f35c78ecd057900c70f3b94da5d8940ad1df005e Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Thu, 28 Nov 2024 00:12:17 +0900 Subject: [PATCH 02/63] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9D=20=EC=8B=9C=20=EC=97=B0=EA=B4=80=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=8F=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/entity/applicant.entity.ts | 2 +- apps/api/src/entity/summary.entity.ts | 2 +- apps/api/src/ticle/ticle.controller.ts | 2 +- apps/api/src/ticle/ticle.service.ts | 11 ++++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/api/src/entity/applicant.entity.ts b/apps/api/src/entity/applicant.entity.ts index f5c33823..8c215112 100644 --- a/apps/api/src/entity/applicant.entity.ts +++ b/apps/api/src/entity/applicant.entity.ts @@ -16,7 +16,7 @@ export class Applicant { @PrimaryGeneratedColumn({ type: 'bigint' }) id: number; - @ManyToOne(() => Ticle, (ticle) => ticle.applicants) + @ManyToOne(() => Ticle, (ticle) => ticle.applicants, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'ticle_id' }) ticle: Ticle; diff --git a/apps/api/src/entity/summary.entity.ts b/apps/api/src/entity/summary.entity.ts index 0986cf52..6cd3f050 100644 --- a/apps/api/src/entity/summary.entity.ts +++ b/apps/api/src/entity/summary.entity.ts @@ -26,7 +26,7 @@ export class Summary { @CreateDateColumn({ type: 'timestamp', name: 'created_at' }) createdAt: Date; - @OneToOne(() => Ticle, (ticle) => ticle.summary) + @OneToOne(() => Ticle, (ticle) => ticle.summary, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'ticle_id' }) ticle: Ticle; } diff --git a/apps/api/src/ticle/ticle.controller.ts b/apps/api/src/ticle/ticle.controller.ts index fc6ca23e..3492e370 100644 --- a/apps/api/src/ticle/ticle.controller.ts +++ b/apps/api/src/ticle/ticle.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Post, Query, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Query, UseGuards, Put } from '@nestjs/common'; import { CreateTicleSchema } from '@repo/types'; import { JwtAuthGuard } from '@/auth/jwt/jwt-auth.guard'; diff --git a/apps/api/src/ticle/ticle.service.ts b/apps/api/src/ticle/ticle.service.ts index f3bfd345..8c0c00f4 100644 --- a/apps/api/src/ticle/ticle.service.ts +++ b/apps/api/src/ticle/ticle.service.ts @@ -229,17 +229,18 @@ export class TicleService { async deleteTicle(userId: number, ticleId: number) { const ticle = await this.ticleRepository.findOne({ - where: { id: ticleId, speaker: { id: userId } }, + where: { id: ticleId }, + relations: ['speaker'], }); - if (ticle.speaker.id === userId) { - throw new BadRequestException(ErrorMessage.CANNOT_DELETE_OTHERS_TICLE); - } - if (!ticle) { throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); } + if (ticle.speaker.id !== userId) { + throw new BadRequestException(ErrorMessage.CANNOT_DELETE_OTHERS_TICLE); + } + await this.ticleRepository.remove(ticle); return; } From a0a067d3817b1fa3a28e5b5d92577e73e980df62 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:26:31 +0900 Subject: [PATCH 03/63] =?UTF-8?q?feat:=20=EB=AA=A8=EB=93=88=EB=B3=84=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?pause/resume=20consumers=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mediasoup/src/events.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/mediasoup/src/events.ts b/packages/mediasoup/src/events.ts index 17fae1b7..5f0eef2e 100644 --- a/packages/mediasoup/src/events.ts +++ b/packages/mediasoup/src/events.ts @@ -9,26 +9,31 @@ export const SOCKET_EVENTS = { reconnectFailed: 'reconnect_failed', // MediaSoup 관련 이벤트 + joinRoom: 'join-room', closeRoom: 'close-room', + createRoom: 'create-room', roomClosed: 'room-closed', + + createTransport: 'create-transport', + connectTransport: 'connect-transport', + newPeer: 'new-peer', - newProducer: 'new-producer', peerLeft: 'peer-left', - consumerClosed: 'consumer-closed', - consumerPaused: 'consumer-paused', + + produce: 'produce', + newProducer: 'new-producer', + getProducers: 'get-producers', closeProducer: 'close-producer', - producerStatusChange: 'producer-status-change', - consumerStatusChange: 'consumer-status-change', + producerClosed: 'producer-closed', producerPaused: 'producer-paused', producerResumed: 'producer-resumed', - producerClosed: 'producer-closed', - createRoom: 'create-room', - joinRoom: 'join-room', - createTransport: 'create-transport', - connectTransport: 'connect-transport', - produce: 'produce', - getProducer: 'get-producer', + producerStatusChange: 'producer-status-change', + consume: 'consume', + consumerClosed: 'consumer-closed', + consumerPaused: 'consumer-paused', + pauseConsumers: 'pause-consumers', + resumeConsumers: 'resume-consumers', } as const; export const TRANSPORT_EVENTS = { From 0d2ec1b35054154e6dc3c7ae87cafe2fc919f206 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:27:01 +0900 Subject: [PATCH 04/63] =?UTF-8?q?feat:=20=EC=86=8C=EC=BC=93=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20Consumer=20pause/resume=20event=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/signaling/signaling.gateway.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/media/src/signaling/signaling.gateway.ts b/apps/media/src/signaling/signaling.gateway.ts index 3bdf45e6..24dd89bf 100644 --- a/apps/media/src/signaling/signaling.gateway.ts +++ b/apps/media/src/signaling/signaling.gateway.ts @@ -104,7 +104,7 @@ export class SignalingGateway implements OnGatewayDisconnect { ); } - @SubscribeMessage(SOCKET_EVENTS.getProducer) + @SubscribeMessage(SOCKET_EVENTS.getProducers) getProducers( @ConnectedSocket() client: Socket, @MessageBody() getProducerDto: server.GetProducersDto @@ -125,7 +125,7 @@ export class SignalingGateway implements OnGatewayDisconnect { @MessageBody('roomId') roomId: string, @MessageBody('producerId') producerId: string ) { - this.mediasoupService.disconnectProducer(roomId, producerId, client.id); + this.mediasoupService.closeProducer(roomId, producerId, client.id); client.to(roomId).emit(SOCKET_EVENTS.producerClosed, { producerId }); } @@ -148,13 +148,21 @@ export class SignalingGateway implements OnGatewayDisconnect { return { producerId }; } - @SubscribeMessage(SOCKET_EVENTS.consumerStatusChange) - pauseConsumer( + @SubscribeMessage(SOCKET_EVENTS.pauseConsumers) + pauseConsumers( @ConnectedSocket() client: Socket, - @MessageBody() changeConsumerState: server.ChangeConsumerStateDto + @MessageBody('roomId') roomId: string, + @MessageBody('consumerIds') consumerIds: string[] + ) { + this.mediasoupService.pauseConsumers(client.id, roomId, consumerIds); + } + + @SubscribeMessage(SOCKET_EVENTS.resumeConsumers) + resumeConsumers( + @ConnectedSocket() client: Socket, + @MessageBody('roomId') roomId: string, + @MessageBody('consumerIds') consumerIds: string[] ) { - const { consumerId } = changeConsumerState; - this.mediasoupService.changeConsumerStatus(client.id, changeConsumerState); - return consumerId; + this.mediasoupService.resumeConsumers(client.id, roomId, consumerIds); } } From c92617074787d3ea1c014138b60fb9e8c169f372 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:38:02 +0900 Subject: [PATCH 05/63] =?UTF-8?q?feat:=20producerId=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20consumer=EB=A5=BC=20=EC=A0=9C=EA=B1=B0=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/room/room.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/media/src/room/room.ts b/apps/media/src/room/room.ts index 26a87e14..639376e9 100644 --- a/apps/media/src/room/room.ts +++ b/apps/media/src/room/room.ts @@ -51,6 +51,10 @@ export class Room { return false; } + removeConsumersByProducerId(producerId: string) { + this.peers.forEach((peer) => peer.removeConsumerByProducerId(producerId)); + } + close() { this.peers.forEach((peer) => peer.close()); this.peers.clear(); From fcb96ce078e7635bb9e41fbd5e0bb0cc8d4f5002 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:38:11 +0900 Subject: [PATCH 06/63] =?UTF-8?q?feat:=20producerId=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20consumer=EB=A5=BC=20=EC=9D=BC=EC=8B=9C=20=EC=A0=95?= =?UTF-8?q?=EC=A7=80,=20=EC=9E=AC=EA=B0=9C=20=EB=B0=8F=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/room/peer.ts | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/apps/media/src/room/peer.ts b/apps/media/src/room/peer.ts index 581327aa..6f222552 100644 --- a/apps/media/src/room/peer.ts +++ b/apps/media/src/room/peer.ts @@ -53,6 +53,57 @@ export class Peer { return this.consumers.get(consumerId); } + deleteProducer(producerId: string) { + const producer = this.producers.get(producerId); + + if (!producer) { + return; + } + + producer.close(); + + this.producers.delete(producerId); + } + + getConsumerByProducerId(producerId: string) { + const consumer = Array.from(this.consumers.values()).find( + (consumer) => consumer.producerId === producerId + ); + + return consumer; + } + + pauseConsumerByProducerId(producerId: string) { + const consumer = this.getConsumerByProducerId(producerId); + + if (!consumer) { + return; + } + + consumer.pause(); + } + + resumeConsumerByProducerId(producerId: string) { + const consumer = this.getConsumerByProducerId(producerId); + + if (!consumer) { + return; + } + + consumer.resume(); + } + + removeConsumerByProducerId(producerId: string) { + const consumer = this.getConsumerByProducerId(producerId); + + if (!consumer) { + return; + } + + consumer.close(); + this.consumers.delete(consumer.id); + } + close() { this.consumers.forEach((consumer) => consumer.close()); this.producers.forEach((producer) => producer.close()); From 061929eae5cbe42855ac959b977d8f7c69c67434 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:39:05 +0900 Subject: [PATCH 07/63] =?UTF-8?q?feat:=20Consumer=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B4=80=EB=A6=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/mediasoup/mediasoup.service.ts | 127 +++++++++++------- 1 file changed, 76 insertions(+), 51 deletions(-) diff --git a/apps/media/src/mediasoup/mediasoup.service.ts b/apps/media/src/mediasoup/mediasoup.service.ts index 5ddba83c..c9154f5d 100644 --- a/apps/media/src/mediasoup/mediasoup.service.ts +++ b/apps/media/src/mediasoup/mediasoup.service.ts @@ -63,9 +63,11 @@ export class MediasoupService implements OnModuleInit { joinRoom(roomId: string, socketId: string, nickname: string) { const room = this.roomService.getRoom(roomId); + if (room.hasPeer(socketId)) { throw new WsException(ErrorMessage.PEER_ALREADY_EXISTS_IN_ROOM); } + room.addPeer(socketId, nickname); return room.getRouter().rtpCapabilities; @@ -75,6 +77,7 @@ export class MediasoupService implements OnModuleInit { const room = this.roomService.getRoom(roomId); const router = room.getRouter(); const transport = await router.createWebRtcTransport(this.mediasoupConfig.webRtcTransport); + room.getPeer(socketId).addTransport(transport); return { @@ -121,6 +124,50 @@ export class MediasoupService implements OnModuleInit { return { nickname: peer.nickname, producerId: producer.id, paused: producer.paused }; } + disconnect(socketId: string) { + const roomIds = this.roomService.deletePeer(socketId); + + return roomIds; + } + + getProducers(roomId: string, socketId: string) { + const room = this.roomService.getRoom(roomId); + + const peers = [...room.peers.values()]; + + const filtered = peers.filter((peer) => peer.socketId !== socketId); + + const result = filtered.flatMap((peer) => + [...peer.producers.values()].map(({ id, kind, appData, paused }) => { + return { + producerId: id, + peerId: peer.socketId, + nickname: peer.nickname, + kind, + appData: appData, + paused, + }; + }) + ); + + return [...new Set(result)]; + } + + changeProducerStatus(socketId: string, changeProducerState: server.ChangeProducerStateDto) { + const { producerId, status, roomId } = changeProducerState; + const room = this.roomService.getRoom(roomId); + const peer = room.peers.get(socketId); + const producer = peer.getProducer(producerId); + + if (status === STREAM_STATUS.pause) { + producer.pause(); + } else { + producer.resume(); + } + + return producerId; + } + async consume( socketId: string, producerId: string, @@ -141,6 +188,7 @@ export class MediasoupService implements OnModuleInit { const consumer = await transport.consume({ producerId, rtpCapabilities, + paused: true, }); consumer.on('producerclose', () => { @@ -158,76 +206,53 @@ export class MediasoupService implements OnModuleInit { }; } - getProducers(roomId: string, socketId: string) { - const room = this.roomService.getRoom(roomId); - - const peers = [...room.peers.values()]; - - const filtered = peers.filter((peer) => peer.socketId !== socketId); - - const result = filtered.flatMap((peer) => - [...peer.producers.values()].map(({ id, kind, appData, paused }) => { - return { - producerId: id, - peerId: peer.socketId, - nickname: peer.nickname, - kind, - appData: appData, - paused, - }; - }) + async createConsumers(socketId: string, targets: server.CreateConsumerDto[]) { + return Promise.all( + targets.map(({ producerId, roomId, transportId, rtpCapabilities }) => + this.consume(socketId, producerId, roomId, transportId, rtpCapabilities) + ) ); - - return [...new Set(result)]; } - disconnect(socketId: string) { - const roomIds = this.roomService.deletePeer(socketId); - - return roomIds; - } - - disconnectProducer(roomId: string, producerId: string, socketId: string) { + closeProducer(roomId: string, producerId: string, socketId: string) { const room = this.roomService.getRoom(roomId); const peer = room.peers.get(socketId); - const producer = peer.getProducer(producerId); - producer.close(); + + peer.deleteProducer(producerId); + room.removeConsumersByProducerId(producerId); + return producerId; } - changeProducerStatus(socketId: string, changeProducerState: server.ChangeProducerStateDto) { - const { producerId, status, roomId } = changeProducerState; + pauseConsumer(socketId: string, consumerId: string, roomId: string) { const room = this.roomService.getRoom(roomId); const peer = room.peers.get(socketId); - const producer = peer.getProducer(producerId); + const consumer = peer.getConsumer(consumerId); - const updateStatus = () => { - if (status === STREAM_STATUS.pause) { - producer.pause(); - } else { - producer.resume(); - } - }; + consumer.pause(); - updateStatus(); - return producerId; + return consumerId; } - changeConsumerStatus(socketId: string, changeConsumerState: server.ChangeConsumerStateDto) { - const { consumerId, status, roomId } = changeConsumerState; + resumeConsumer(socketId: string, consumerId: string, roomId: string) { const room = this.roomService.getRoom(roomId); const peer = room.peers.get(socketId); const consumer = peer.getConsumer(consumerId); - const updateStatus = () => { - if (status === STREAM_STATUS.pause) { - consumer.pause(); - } else { - consumer.resume(); - } - }; + consumer.resume(); - updateStatus(); return consumerId; } + + pauseConsumers(socketId: string, roomId: string, consumerIds: string[]) { + consumerIds.forEach((consumerId) => { + this.pauseConsumer(socketId, consumerId, roomId); + }); + } + + resumeConsumers(socketId: string, roomId: string, consumerIds: string[]) { + consumerIds.forEach((consumerId) => { + this.resumeConsumer(socketId, consumerId, roomId); + }); + } } From 2ce474cc863bc9d6ae84b7edf1b9f0519eae5d36 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:39:12 +0900 Subject: [PATCH 08/63] =?UTF-8?q?feat:=20consume=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=9E=AC=EA=B5=AC=EC=84=B1=20=EB=B0=8F=20createCon?= =?UTF-8?q?sumers=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/signaling/signaling.gateway.ts | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/media/src/signaling/signaling.gateway.ts b/apps/media/src/signaling/signaling.gateway.ts index 24dd89bf..191b7a4d 100644 --- a/apps/media/src/signaling/signaling.gateway.ts +++ b/apps/media/src/signaling/signaling.gateway.ts @@ -88,22 +88,6 @@ export class SignalingGateway implements OnGatewayDisconnect { return createProducerRes; } - @SubscribeMessage(SOCKET_EVENTS.consume) - async handleConsume( - @ConnectedSocket() client: Socket, - @MessageBody() createConsumerDto: server.CreateConsumerDto - ): Promise { - const { transportId, producerId, roomId, rtpCapabilities } = createConsumerDto; - - return this.mediasoupService.consume( - client.id, - producerId, - roomId, - transportId, - rtpCapabilities - ); - } - @SubscribeMessage(SOCKET_EVENTS.getProducers) getProducers( @ConnectedSocket() client: Socket, @@ -148,6 +132,30 @@ export class SignalingGateway implements OnGatewayDisconnect { return { producerId }; } + @SubscribeMessage(SOCKET_EVENTS.consume) + async handleConsume( + @ConnectedSocket() client: Socket, + @MessageBody() createConsumerDto: server.CreateConsumerDto + ): Promise { + const { transportId, producerId, roomId, rtpCapabilities } = createConsumerDto; + + return this.mediasoupService.consume( + client.id, + producerId, + roomId, + transportId, + rtpCapabilities + ); + } + + @SubscribeMessage(SOCKET_EVENTS.createConsumers) + async createConsumers( + @ConnectedSocket() client: Socket, + @MessageBody() createConsumersDto: server.CreateConsumerDto[] + ) { + await this.mediasoupService.createConsumers(client.id, createConsumersDto); + } + @SubscribeMessage(SOCKET_EVENTS.pauseConsumers) pauseConsumers( @ConnectedSocket() client: Socket, From 6b575026e940074bc26771bb82af5d5a305547f2 Mon Sep 17 00:00:00 2001 From: seoko97 Date: Thu, 28 Nov 2024 00:39:16 +0900 Subject: [PATCH 09/63] =?UTF-8?q?feat:=20createConsumers=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mediasoup/src/events.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mediasoup/src/events.ts b/packages/mediasoup/src/events.ts index 5f0eef2e..1c2f4236 100644 --- a/packages/mediasoup/src/events.ts +++ b/packages/mediasoup/src/events.ts @@ -30,6 +30,7 @@ export const SOCKET_EVENTS = { producerStatusChange: 'producer-status-change', consume: 'consume', + createConsumers: 'create-consumers', consumerClosed: 'consumer-closed', consumerPaused: 'consumer-paused', pauseConsumers: 'pause-consumers', From e72428fb63fa34c0e3e36c1c14bc1d1d07b76c37 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Thu, 28 Nov 2024 01:00:29 +0900 Subject: [PATCH 10/63] =?UTF-8?q?feat:=20=EC=98=B5=EC=85=98=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20portal=EB=A1=9C=20=EB=9D=84=EC=9B=8C?= =?UTF-8?q?=EC=84=9C=20=EB=8B=A4=EB=A5=B8=20=EC=9A=94=EC=86=8C=EC=97=90=20?= =?UTF-8?q?=EC=98=81=ED=96=A5=EB=B0=9B=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/Select/index.tsx | 89 ++++++++++++++----- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/apps/web/src/components/common/Select/index.tsx b/apps/web/src/components/common/Select/index.tsx index 957200ed..5fd950b1 100644 --- a/apps/web/src/components/common/Select/index.tsx +++ b/apps/web/src/components/common/Select/index.tsx @@ -1,10 +1,12 @@ import { cva } from 'class-variance-authority'; -import { useRef, useState, KeyboardEvent } from 'react'; +import { useRef, useState, KeyboardEvent, useLayoutEffect } from 'react'; import ChevronDownIc from '@/assets/icons/chevron-down.svg?react'; import ChevronUpIc from '@/assets/icons/chevron-up.svg?react'; import useOutsideClick from '@/hooks/useOutsideClick'; +import Portal from '../Portal'; + const selectVariants = cva( 'flex w-full cursor-pointer items-center justify-between gap-3.5 rounded-base bg-white px-3.5 py-2.5 text-body1 text-main', { @@ -24,6 +26,12 @@ export interface Option { label: string; value: string; } + +interface Position { + top: number; + left: number; + width: number; +} interface Select { options: Option[]; placeholder?: string; @@ -33,6 +41,41 @@ interface Select { function Select({ options, placeholder, selectedOption, onChange }: Select) { const [isOpen, setIsOpen] = useState(false); + const [position, setPosition] = useState({ + top: 0, + left: 0, + width: 0, + }); + + const selectRef = useRef(null); + + const updatePosition = () => { + if (!selectRef.current) return; + + const selectRect = selectRef.current.getBoundingClientRect(); + const scrollTop = window.scrollY || document.documentElement.scrollTop; + const scrollLeft = window.scrollX || document.documentElement.scrollLeft; + + setPosition({ + top: selectRect.bottom + scrollTop, + left: selectRect.left + scrollLeft, + width: selectRect.width, + }); + }; + + useLayoutEffect(() => { + if (!isOpen) return; + + updatePosition(); + + window.addEventListener('scroll', updatePosition); + window.addEventListener('resize', updatePosition); + + return () => { + window.removeEventListener('scroll', updatePosition); + window.removeEventListener('resize', updatePosition); + }; + }, [isOpen]); const handleOptionChange = (option: Option) => { onChange?.(option); @@ -55,7 +98,6 @@ function Select({ options, placeholder, selectedOption, onChange }: Select) { handleOptionChange(option); }; - const selectRef = useRef(null); useOutsideClick(selectRef, handleSelectClose); return ( @@ -69,24 +111,31 @@ function Select({ options, placeholder, selectedOption, onChange }: Select) { {isOpen ?