From e0aa59083d759c0ef1818a274a7e6229b460b893 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 15 May 2023 21:53:28 +0900 Subject: [PATCH 01/27] =?UTF-8?q?chat=E6=A9=9F=E8=83=BD=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE=E4=B8=80?= =?UTF-8?q?=E9=80=A3=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92?= =?UTF-8?q?=E8=87=AA=E5=8B=95=E7=94=9F=E6=88=90=E3=81=97=E3=81=9F=20nest?= =?UTF-8?q?=20g=20module=20chat=20nest=20g=20service=20chat=20nest=20g=20g?= =?UTF-8?q?ateway=20chat=20nest=20g=20controller=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/app.module.ts | 3 ++- backend/src/chat/chat.controller.spec.ts | 18 ++++++++++++++++++ backend/src/chat/chat.controller.ts | 4 ++++ backend/src/chat/chat.gateway.spec.ts | 18 ++++++++++++++++++ backend/src/chat/chat.gateway.ts | 9 +++++++++ backend/src/chat/chat.module.ts | 11 +++++++++++ backend/src/chat/chat.service.spec.ts | 18 ++++++++++++++++++ backend/src/chat/chat.service.ts | 4 ++++ 8 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 backend/src/chat/chat.controller.spec.ts create mode 100644 backend/src/chat/chat.controller.ts create mode 100644 backend/src/chat/chat.gateway.spec.ts create mode 100644 backend/src/chat/chat.gateway.ts create mode 100644 backend/src/chat/chat.module.ts create mode 100644 backend/src/chat/chat.service.spec.ts create mode 100644 backend/src/chat/chat.service.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index d69ed6a0b..411fe1ba3 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -7,9 +7,10 @@ import { EventsGateway } from './events/events.gateway'; import { PostMessageModule } from './post-message/post-message.module'; import { PrismaModule } from './prisma/prisma.module'; import { UserModule } from './user/user.module'; +import { ChatModule } from './chat/chat.module'; @Module({ - imports: [PrismaModule, PostMessageModule, ConfigModule, UserModule], + imports: [PrismaModule, PostMessageModule, ConfigModule, UserModule, ChatModule], controllers: [AppController], providers: [AppService, EventsGateway], }) diff --git a/backend/src/chat/chat.controller.spec.ts b/backend/src/chat/chat.controller.spec.ts new file mode 100644 index 000000000..571463df3 --- /dev/null +++ b/backend/src/chat/chat.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ChatController } from './chat.controller'; + +describe('ChatController', () => { + let controller: ChatController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ChatController], + }).compile(); + + controller = module.get(ChatController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/src/chat/chat.controller.ts b/backend/src/chat/chat.controller.ts new file mode 100644 index 000000000..d71b71a9a --- /dev/null +++ b/backend/src/chat/chat.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('chat') +export class ChatController {} diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts new file mode 100644 index 000000000..34daca947 --- /dev/null +++ b/backend/src/chat/chat.gateway.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ChatGateway } from './chat.gateway'; + +describe('ChatGateway', () => { + let gateway: ChatGateway; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ChatGateway], + }).compile(); + + gateway = module.get(ChatGateway); + }); + + it('should be defined', () => { + expect(gateway).toBeDefined(); + }); +}); diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts new file mode 100644 index 000000000..f505d25cc --- /dev/null +++ b/backend/src/chat/chat.gateway.ts @@ -0,0 +1,9 @@ +import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; + +@WebSocketGateway() +export class ChatGateway { + @SubscribeMessage('message') + handleMessage(client: any, payload: any): string { + return 'Hello world!'; + } +} diff --git a/backend/src/chat/chat.module.ts b/backend/src/chat/chat.module.ts new file mode 100644 index 000000000..19259b6fa --- /dev/null +++ b/backend/src/chat/chat.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ChatService } from './chat.service'; +import { ChatGateway } from './chat.gateway'; +import { ChatController } from './chat.controller'; + + +@Module({ + providers: [ChatService, ChatGateway], + controllers: [ChatController], +}) +export class ChatModule {} diff --git a/backend/src/chat/chat.service.spec.ts b/backend/src/chat/chat.service.spec.ts new file mode 100644 index 000000000..110cd7d3d --- /dev/null +++ b/backend/src/chat/chat.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ChatService } from './chat.service'; + +describe('ChatService', () => { + let service: ChatService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ChatService], + }).compile(); + + service = module.get(ChatService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/chat/chat.service.ts b/backend/src/chat/chat.service.ts new file mode 100644 index 000000000..408edccb1 --- /dev/null +++ b/backend/src/chat/chat.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ChatService {} From 5a1f93939a638fed20f7906ef9236df7c9474e32 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 15 May 2023 23:13:33 +0900 Subject: [PATCH 02/27] =?UTF-8?q?=E8=A8=B1=E5=8F=AF=E3=81=99=E3=82=8Borigi?= =?UTF-8?q?n,=20websocket=E3=81=B8=E3=81=AE=E6=8E=A5=E7=B6=9A=E3=80=81?= =?UTF-8?q?=E5=88=87=E6=96=AD=E6=99=82=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index f505d25cc..30a36313d 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,9 +1,17 @@ import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; +import { Socket } from 'socket.io'; -@WebSocketGateway() +@WebSocketGateway({ + cors: { + origin: '*', + }, +}) export class ChatGateway { - @SubscribeMessage('message') - handleMessage(client: any, payload: any): string { - return 'Hello world!'; + + handleConnection(client: Socket) { + console.log('chat Connection'); + } + handleDisconnect(client: Socket) { + console.log('chat Disconnection'); } } From 6d8745a83e7eac41045e22fc1a7abe8207807753 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 01:31:04 +0900 Subject: [PATCH 03/27] =?UTF-8?q?message=E3=82=92db=E3=81=AB=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 14 ++++++++++++-- backend/src/chat/chat.module.ts | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 30a36313d..a627db6b9 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,17 +1,27 @@ import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; import { Socket } from 'socket.io'; - +import { PrismaService } from '../prisma/prisma.service'; @WebSocketGateway({ cors: { origin: '*', }, }) export class ChatGateway { - + constructor(private prisma: PrismaService) {} handleConnection(client: Socket) { console.log('chat Connection'); } handleDisconnect(client: Socket) { console.log('chat Disconnection'); } + @SubscribeMessage('sendMessage') + async sendMessage(client: Socket) { + await this.prisma.message.create({ + data: { + content: 'test', + userId: 1, + chatRoomId: 1, + }, + }); + } } diff --git a/backend/src/chat/chat.module.ts b/backend/src/chat/chat.module.ts index 19259b6fa..18b8b2e28 100644 --- a/backend/src/chat/chat.module.ts +++ b/backend/src/chat/chat.module.ts @@ -2,9 +2,11 @@ import { Module } from '@nestjs/common'; import { ChatService } from './chat.service'; import { ChatGateway } from './chat.gateway'; import { ChatController } from './chat.controller'; +import { PrismaModule } from '../prisma/prisma.module'; @Module({ + imports: [PrismaModule], providers: [ChatService, ChatGateway], controllers: [ChatController], }) From ad4f6a9281b3ec61f1fd79311edd450745fa206f Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 01:38:20 +0900 Subject: [PATCH 04/27] =?UTF-8?q?message=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=8F=96=E3=82=8Bdto=E3=82=92=E4=BD=9C=E6=88=90=E3=80=81?= =?UTF-8?q?=E3=81=9D=E3=82=8C=E3=82=92=E5=89=B2=E3=82=8A=E5=BD=93=E3=81=A6?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 9 +++++---- backend/src/chat/dto/message.dto.ts | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 backend/src/chat/dto/message.dto.ts diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index a627db6b9..a46444e1b 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,6 +1,7 @@ import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; import { Socket } from 'socket.io'; import { PrismaService } from '../prisma/prisma.service'; +import { MessageDto } from './dto/message.dto'; @WebSocketGateway({ cors: { origin: '*', @@ -15,12 +16,12 @@ export class ChatGateway { console.log('chat Disconnection'); } @SubscribeMessage('sendMessage') - async sendMessage(client: Socket) { + async sendMessage(client: Socket, messageDto: MessageDto) { await this.prisma.message.create({ data: { - content: 'test', - userId: 1, - chatRoomId: 1, + content: messageDto.content, + userId: messageDto.userId, + chatRoomId: messageDto.chatRoomId, }, }); } diff --git a/backend/src/chat/dto/message.dto.ts b/backend/src/chat/dto/message.dto.ts new file mode 100644 index 000000000..36c673f54 --- /dev/null +++ b/backend/src/chat/dto/message.dto.ts @@ -0,0 +1,5 @@ +export class MessageDto { + content: string; + userId: number; + chatRoomId: number; +} From b2638be5e9d861dacbbecdcc5754bc79ee845d09 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 01:56:31 +0900 Subject: [PATCH 05/27] =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88?= =?UTF-8?q?=E3=81=9A=E6=8E=A5=E7=B6=9A=E3=81=97=E3=81=9F=E5=85=A8=E5=93=A1?= =?UTF-8?q?=E3=81=AB=E8=BF=94=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index a46444e1b..d9648582e 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,5 +1,5 @@ -import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets'; -import { Socket } from 'socket.io'; +import { SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; +import { Socket,Server } from 'socket.io'; import { PrismaService } from '../prisma/prisma.service'; import { MessageDto } from './dto/message.dto'; @WebSocketGateway({ @@ -8,6 +8,8 @@ import { MessageDto } from './dto/message.dto'; }, }) export class ChatGateway { + @WebSocketServer() + server: Server; constructor(private prisma: PrismaService) {} handleConnection(client: Socket) { console.log('chat Connection'); @@ -17,12 +19,13 @@ export class ChatGateway { } @SubscribeMessage('sendMessage') async sendMessage(client: Socket, messageDto: MessageDto) { - await this.prisma.message.create({ + const msg = await this.prisma.message.create({ data: { content: messageDto.content, userId: messageDto.userId, chatRoomId: messageDto.chatRoomId, }, }); + this.server.emit('sendMessage', msg); } } From c293e713e6a2f263305b1d5dd703575a6ab12598 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:02:17 +0900 Subject: [PATCH 06/27] =?UTF-8?q?Channel=E3=81=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E9=96=A2=E6=95=B0=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index d9648582e..960b5e2ae 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -17,6 +17,16 @@ export class ChatGateway { handleDisconnect(client: Socket) { console.log('chat Disconnection'); } + @SubscribeMessage('joinChannel') + async joinChannel(client: Socket){ + await this.prisma.roomMember.create({ + data: { + userId: 1, + chatRoomId: 1, + }, + }); + } + @SubscribeMessage('sendMessage') async sendMessage(client: Socket, messageDto: MessageDto) { const msg = await this.prisma.message.create({ From 30574a76f5584b0b1176cd895960a2528631ace2 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:08:40 +0900 Subject: [PATCH 07/27] =?UTF-8?q?user=E3=82=92channel=E3=81=AB=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=81=99=E3=82=8B=E7=94=A8=E3=81=AEdto=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 7 ++++--- backend/src/chat/dto/Channel.dto.ts | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 backend/src/chat/dto/Channel.dto.ts diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 960b5e2ae..9051af961 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -2,6 +2,7 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/web import { Socket,Server } from 'socket.io'; import { PrismaService } from '../prisma/prisma.service'; import { MessageDto } from './dto/message.dto'; +import { JoinChannelDto } from './dto/Channel.dto'; @WebSocketGateway({ cors: { origin: '*', @@ -18,11 +19,11 @@ export class ChatGateway { console.log('chat Disconnection'); } @SubscribeMessage('joinChannel') - async joinChannel(client: Socket){ + async joinChannel(client: Socket, joinChannelDto: JoinChannelDto) { await this.prisma.roomMember.create({ data: { - userId: 1, - chatRoomId: 1, + userId: joinChannelDto.userId, + chatRoomId: joinChannelDto.chatRoomId, }, }); } diff --git a/backend/src/chat/dto/Channel.dto.ts b/backend/src/chat/dto/Channel.dto.ts new file mode 100644 index 000000000..52257cf63 --- /dev/null +++ b/backend/src/chat/dto/Channel.dto.ts @@ -0,0 +1,4 @@ +export class JoinChannelDto { + chatRoomId: number; + userId: number; +} From ab8ca32724935460328a778380928fc298129cb8 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:26:51 +0900 Subject: [PATCH 08/27] =?UTF-8?q?secket.io=E3=81=A7Channel=E3=82=92?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E3=81=97=E3=81=A6=E3=81=9D=E3=81=93=E3=81=AB?= =?UTF-8?q?=E5=8F=82=E5=8A=A0=E3=81=97=E3=81=A6=E3=82=8Buser=E3=81=ABmsg?= =?UTF-8?q?=E3=82=92=E8=BF=94=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 9051af961..d95343e01 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,5 +1,5 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; -import { Socket,Server } from 'socket.io'; +import { Socket, Server } from 'socket.io'; import { PrismaService } from '../prisma/prisma.service'; import { MessageDto } from './dto/message.dto'; import { JoinChannelDto } from './dto/Channel.dto'; @@ -13,6 +13,7 @@ export class ChatGateway { server: Server; constructor(private prisma: PrismaService) {} handleConnection(client: Socket) { + // TODO jwtができたら接続時にdbに保存されてる所属しているチャンネルに全てにclient.joinする console.log('chat Connection'); } handleDisconnect(client: Socket) { @@ -20,12 +21,13 @@ export class ChatGateway { } @SubscribeMessage('joinChannel') async joinChannel(client: Socket, joinChannelDto: JoinChannelDto) { - await this.prisma.roomMember.create({ + const addedUser = await this.prisma.roomMember.create({ data: { userId: joinChannelDto.userId, chatRoomId: joinChannelDto.chatRoomId, }, }); + client.join(addedUser.chatRoomId.toString()); } @SubscribeMessage('sendMessage') @@ -37,6 +39,6 @@ export class ChatGateway { chatRoomId: messageDto.chatRoomId, }, }); - this.server.emit('sendMessage', msg); + this.server.to(msg.chatRoomId.toString()).emit('sendMessage', msg); } } From 17f4c3eafb9280b4a1463f61db9f3bd72f79d4d7 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:39:03 +0900 Subject: [PATCH 09/27] =?UTF-8?q?channel=E3=82=92=E4=BD=9C=E3=82=8B?= =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=82=92=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index d95343e01..dd431d0dd 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -19,6 +19,15 @@ export class ChatGateway { handleDisconnect(client: Socket) { console.log('chat Disconnection'); } + @SubscribeMessage('createChannel') + async createChannel(client:Socket){ + await this.prisma.chatRoom.create({ + data: { + roomName: 'hogeroom', + }, + }); + } + @SubscribeMessage('joinChannel') async joinChannel(client: Socket, joinChannelDto: JoinChannelDto) { const addedUser = await this.prisma.roomMember.create({ From 1b2c44926a76ea76f992a056e7485f97053c64c0 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:42:31 +0900 Subject: [PATCH 10/27] =?UTF-8?q?channel=E4=BD=9C=E6=88=90=E3=81=AEdto?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85=E3=81=9D=E3=82=8C=E3=81=AB=E4=BC=B4?= =?UTF-8?q?=E3=81=84createChannel=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 6 +++--- backend/src/chat/dto/Channel.dto.ts | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index dd431d0dd..f0fcdbce2 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -2,7 +2,7 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/web import { Socket, Server } from 'socket.io'; import { PrismaService } from '../prisma/prisma.service'; import { MessageDto } from './dto/message.dto'; -import { JoinChannelDto } from './dto/Channel.dto'; +import { CreateChannelDto, JoinChannelDto } from './dto/Channel.dto'; @WebSocketGateway({ cors: { origin: '*', @@ -20,10 +20,10 @@ export class ChatGateway { console.log('chat Disconnection'); } @SubscribeMessage('createChannel') - async createChannel(client:Socket){ + async createChannel(client:Socket, createChannelDto: CreateChannelDto){ await this.prisma.chatRoom.create({ data: { - roomName: 'hogeroom', + roomName: createChannelDto.roomName, }, }); } diff --git a/backend/src/chat/dto/Channel.dto.ts b/backend/src/chat/dto/Channel.dto.ts index 52257cf63..d33a12be2 100644 --- a/backend/src/chat/dto/Channel.dto.ts +++ b/backend/src/chat/dto/Channel.dto.ts @@ -2,3 +2,7 @@ export class JoinChannelDto { chatRoomId: number; userId: number; } +export class CreateChannelDto { + roomName: string; + userId: number; +} From aa456082420a9cba55977ece1182e06e9a6e5096 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 16 May 2023 02:49:38 +0900 Subject: [PATCH 11/27] =?UTF-8?q?channel=E3=82=92=E4=BD=9C=E3=81=A3?= =?UTF-8?q?=E3=81=9F=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92roomMembe?= =?UTF-8?q?r=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index f0fcdbce2..8f8c47778 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -21,11 +21,18 @@ export class ChatGateway { } @SubscribeMessage('createChannel') async createChannel(client:Socket, createChannelDto: CreateChannelDto){ - await this.prisma.chatRoom.create({ + const createdRoom = await this.prisma.chatRoom.create({ data: { roomName: createChannelDto.roomName, }, }); + await this.prisma.roomMember.create({ + data: { + userId: createChannelDto.userId, + chatRoomId: createdRoom.id, + }, + }); + client.join(createdRoom.id.toString()); } @SubscribeMessage('joinChannel') From 3a71f058e77396545dcd63b15ac2f524a4ac8918 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 17 May 2023 17:54:14 +0900 Subject: [PATCH 12/27] =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E7=99=BA=E7=81=AB=E3=81=95=E3=81=9B=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 8f8c47778..893ae6f73 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -33,6 +33,7 @@ export class ChatGateway { }, }); client.join(createdRoom.id.toString()); + this.server.to(createdRoom.id.toString()).emit('createChannel', createdRoom); } @SubscribeMessage('joinChannel') @@ -44,6 +45,7 @@ export class ChatGateway { }, }); client.join(addedUser.chatRoomId.toString()); + this.server.to(addedUser.chatRoomId.toString()).emit('joinChannel', addedUser); } @SubscribeMessage('sendMessage') From f7cbd41257cb56fff704ffc095e75f660c629619 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 17 May 2023 17:59:47 +0900 Subject: [PATCH 13/27] =?UTF-8?q?fmt=20+=20lint=E3=81=AE=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=97=E3=81=9F=20handle?= =?UTF-8?q?Connection=E3=81=A8handleDisconnect=E3=81=A7=20Client:Socket?= =?UTF-8?q?=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=E3=81=AA=E3=81=8B?= =?UTF-8?q?=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7console.log=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/app.module.ts | 8 +++++++- backend/src/chat/chat.controller.spec.ts | 1 + backend/src/chat/chat.gateway.spec.ts | 1 + backend/src/chat/chat.gateway.ts | 21 +++++++++++++++++---- backend/src/chat/chat.module.ts | 5 +++-- backend/src/chat/chat.service.spec.ts | 1 + 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 411fe1ba3..7b2ca6f3f 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -10,7 +10,13 @@ import { UserModule } from './user/user.module'; import { ChatModule } from './chat/chat.module'; @Module({ - imports: [PrismaModule, PostMessageModule, ConfigModule, UserModule, ChatModule], + imports: [ + PrismaModule, + PostMessageModule, + ConfigModule, + UserModule, + ChatModule, + ], controllers: [AppController], providers: [AppService, EventsGateway], }) diff --git a/backend/src/chat/chat.controller.spec.ts b/backend/src/chat/chat.controller.spec.ts index 571463df3..fb5699998 100644 --- a/backend/src/chat/chat.controller.spec.ts +++ b/backend/src/chat/chat.controller.spec.ts @@ -1,4 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; + import { ChatController } from './chat.controller'; describe('ChatController', () => { diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index 34daca947..9ff3dc2a6 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -1,4 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; + import { ChatGateway } from './chat.gateway'; describe('ChatGateway', () => { diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 893ae6f73..2711a332a 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -1,6 +1,12 @@ -import { SubscribeMessage, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; +import { + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; import { Socket, Server } from 'socket.io'; + import { PrismaService } from '../prisma/prisma.service'; + import { MessageDto } from './dto/message.dto'; import { CreateChannelDto, JoinChannelDto } from './dto/Channel.dto'; @WebSocketGateway({ @@ -15,12 +21,15 @@ export class ChatGateway { handleConnection(client: Socket) { // TODO jwtができたら接続時にdbに保存されてる所属しているチャンネルに全てにclient.joinする console.log('chat Connection'); + console.log(client.id); } + handleDisconnect(client: Socket) { console.log('chat Disconnection'); + console.log(client.id); } @SubscribeMessage('createChannel') - async createChannel(client:Socket, createChannelDto: CreateChannelDto){ + async createChannel(client: Socket, createChannelDto: CreateChannelDto) { const createdRoom = await this.prisma.chatRoom.create({ data: { roomName: createChannelDto.roomName, @@ -33,7 +42,9 @@ export class ChatGateway { }, }); client.join(createdRoom.id.toString()); - this.server.to(createdRoom.id.toString()).emit('createChannel', createdRoom); + this.server + .to(createdRoom.id.toString()) + .emit('createChannel', createdRoom); } @SubscribeMessage('joinChannel') @@ -45,7 +56,9 @@ export class ChatGateway { }, }); client.join(addedUser.chatRoomId.toString()); - this.server.to(addedUser.chatRoomId.toString()).emit('joinChannel', addedUser); + this.server + .to(addedUser.chatRoomId.toString()) + .emit('joinChannel', addedUser); } @SubscribeMessage('sendMessage') diff --git a/backend/src/chat/chat.module.ts b/backend/src/chat/chat.module.ts index 18b8b2e28..59398df5c 100644 --- a/backend/src/chat/chat.module.ts +++ b/backend/src/chat/chat.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; + +import { PrismaModule } from '../prisma/prisma.module'; + import { ChatService } from './chat.service'; import { ChatGateway } from './chat.gateway'; import { ChatController } from './chat.controller'; -import { PrismaModule } from '../prisma/prisma.module'; - @Module({ imports: [PrismaModule], diff --git a/backend/src/chat/chat.service.spec.ts b/backend/src/chat/chat.service.spec.ts index 110cd7d3d..8783c8727 100644 --- a/backend/src/chat/chat.service.spec.ts +++ b/backend/src/chat/chat.service.spec.ts @@ -1,4 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; + import { ChatService } from './chat.service'; describe('ChatService', () => { From e6bcfee9716cc633e9c43ceb3afcdc2cc0c456d2 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 17 May 2023 18:00:37 +0900 Subject: [PATCH 14/27] =?UTF-8?q?test=E3=81=A7socket.io-client=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=81=AE=E3=81=A7install=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package-lock.json | 37 ++++++++++++++++++++++++++++++++++++- backend/package.json | 3 ++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index eed5fbcdd..12bda8fc6 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,7 +18,8 @@ "@nestjs/websockets": "^9.4.0", "@prisma/client": "^4.12.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "socket.io-client": "^4.6.1" }, "devDependencies": { "@nestjs/cli": "^9.0.0", @@ -3681,6 +3682,18 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "node_modules/engine.io-parser": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", @@ -7898,6 +7911,20 @@ "ws": "~8.11.0" } }, + "node_modules/socket.io-client": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", + "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", @@ -9040,6 +9067,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index 8dd139f7e..41fd79ddf 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,7 +31,8 @@ "@nestjs/websockets": "^9.4.0", "@prisma/client": "^4.12.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "socket.io-client": "^4.6.1" }, "devDependencies": { "@nestjs/cli": "^9.0.0", From adc6787239d5a624bdd8c8a83c59c3c31557a4ee Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 17 May 2023 18:01:12 +0900 Subject: [PATCH 15/27] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=8B?= =?UTF-8?q?=E3=81=84=E3=81=9F=E3=80=80=E6=AD=A3=E5=B8=B8=E7=B3=BB=E3=81=AE?= =?UTF-8?q?=E3=81=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 178 +++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 4 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index 9ff3dc2a6..bcd80cb74 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -1,19 +1,189 @@ import { Test, TestingModule } from '@nestjs/testing'; +import { io, Socket } from 'socket.io-client'; +import { ChatRoom, Message, RoomMember, User } from '@prisma/client'; +import { IoAdapter } from '@nestjs/platform-socket.io'; +import { INestApplication } from '@nestjs/common'; +import { TestModule } from '../test/test.module'; +import { PrismaService } from '../prisma/prisma.service'; + +import { CreateChannelDto, JoinChannelDto } from './dto/Channel.dto'; import { ChatGateway } from './chat.gateway'; +import { MessageDto } from './dto/message.dto'; +const USERNUM = 10; describe('ChatGateway', () => { let gateway: ChatGateway; - - beforeEach(async () => { + let prismaService: PrismaService; + let clientSocket: Socket[]; + let users: User[]; + let room: ChatRoom | null; + let app: INestApplication; + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ChatGateway], + imports: [TestModule], + providers: [ChatGateway, PrismaService], }).compile(); - gateway = module.get(ChatGateway); + prismaService = module.get(PrismaService); + app = module.createNestApplication(); + app.useWebSocketAdapter(new IoAdapter(app)); + await app.listen(8001); + clientSocket = []; + users = []; + for (let i = 0; i < USERNUM; i++) { + const sock: Socket = io('http://localhost:8001'); + clientSocket.push(sock); + } + for (let i = 0; i < USERNUM; i++) { + const user = await prismaService.user.upsert({ + where: { + email: `chatTestUser${i}@test.com`, + }, + update: {}, + create: { + email: `chatTestUser${i}@test.com`, + username: `chatTestUser${i}`, + hashedPassword: `chatTestUser${i}`, + }, + }); + users.push(user); + } + for (let i = 0; i < USERNUM; i++) { + clientSocket[i].on('connect', () => { + console.log(`connected ${i}`); + }); + } + }); + afterAll(async () => { + for (let i = 0; i < USERNUM; i++) { + clientSocket[i].off('connect', () => { + console.log(`dissconnected ${i}`); + }); + } + await app.close(); + for (let i = 0; i < USERNUM; i++) { + clientSocket[i].disconnect(); + } + await prismaService.chatRoom.delete({ + where: { + roomName: room?.roomName || 'testroom', + }, + }); + for (let i = 0; i < USERNUM; i++) { + await prismaService.user.delete({ + where: { + id: users[i].id, + }, + }); + } }); it('should be defined', () => { expect(gateway).toBeDefined(); }); + describe('create Channel', () => { + //users[0]が部屋を作成 + it('should be create Channel successfully', (done) => { + const createChannelDto: CreateChannelDto = { + roomName: 'testroom', + userId: users[0].id, + }; + clientSocket[0].on('createChannel', async () => { + room = await prismaService.chatRoom.findFirst({ + where: { + roomName: 'testroom', + }, + }); + expect(room?.roomName).toEqual(createChannelDto.roomName); + done(); + }); + clientSocket[0].emit('createChannel', createChannelDto); + }); + }); + + describe('join Channel', () => { + let roomId: number; + beforeEach(() => { + if (!room) { + throw Error('room is not created'); + } + roomId = room.id; + }); + // users[1]~users[9]が部屋に参加 + it('should be join room successfully', async () => { + const promise = []; + let roomMemberList: RoomMember[] = []; + for (let i = 1; i < USERNUM; i++) { + const joinChannel: JoinChannelDto = { + userId: users[i].id, + chatRoomId: roomId, + }; + const joinPromise = new Promise((resolve) => { + clientSocket[i].on('joinChannel', async () => { + const newRoomMemberList = await prismaService.roomMember.findMany({ + where: { + chatRoomId: roomId, + }, + }); + roomMemberList = + roomMemberList.length < newRoomMemberList.length + ? newRoomMemberList + : roomMemberList; + resolve(null); + }); + clientSocket[i].emit('joinChannel', joinChannel); + }); + promise.push(joinPromise); + } + await Promise.all(promise); + expect(roomMemberList.length).toEqual(USERNUM); + }); + }); + describe('sendMessage', () => { + let roomId: number; + beforeEach(() => { + if (!room) { + throw Error('room is not created'); + } + roomId = room.id; + }); + // users[0]が送信したメッセージがDBに保存されるか + it('stores user message in db successfully', (done) => { + const messageDto: MessageDto = { + content: 'test message', + userId: users[0].id, + chatRoomId: roomId, + }; + clientSocket[0].on('sendMessage', async () => { + const dbMsg = await prismaService.message.findFirst({ + where: { + chatRoomId: roomId, + }, + }); + expect(dbMsg?.content).toEqual(messageDto.content); + done(); + }); + clientSocket[0].emit('sendMessage', messageDto); + }); + // users[0]が送信したメッセージが全員に送信されるか + it('sends user message to all 1 in the room', (done) => { + const messageDto: MessageDto = { + content: 'test message', + userId: users[0].id, + chatRoomId: roomId, + }; + let receivedCount = 0; + for (let i = 0; i < USERNUM; i++) { + clientSocket[i].on('sendMessage', (emitMsg: Message) => { + expect(emitMsg.content).toEqual(messageDto.content); + receivedCount++; + if (receivedCount === USERNUM) { + done(); + } + }); + } + clientSocket[0].emit('sendMessage', messageDto); + }); + }); }); From 3fbcedcbcf602afe3ad268ee498462b66c316b0f Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Sun, 28 May 2023 16:11:26 +0900 Subject: [PATCH 16/27] =?UTF-8?q?dependencies=E3=81=A7install=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=81=9F=E3=81=AE=E3=82=92uninstall=E3=81=97=E3=81=A6?= =?UTF-8?q?dev=E3=81=A7install=E3=81=97=E3=81=9F=20npm=20uninstall=20socke?= =?UTF-8?q?t.io-client=20npm=20install=20=20--save-dev=20socket.io-client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package-lock.json | 7 +++++-- backend/package.json | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 89fe3e5e2..44ae1d2c4 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,8 +18,7 @@ "@nestjs/websockets": "^9.4.0", "@prisma/client": "^4.12.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "socket.io-client": "^4.6.1" + "rxjs": "^7.2.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", @@ -38,6 +37,7 @@ "jest": "29.5.0", "prettier": "^2.3.2", "prisma": "^4.12.0", + "socket.io-client": "^4.6.1", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "29.0.5", @@ -3686,6 +3686,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", @@ -7915,6 +7916,7 @@ "version": "4.6.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -9071,6 +9073,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, "engines": { "node": ">=0.4.0" } diff --git a/backend/package.json b/backend/package.json index 7be6eac0a..f718a8610 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,8 +31,7 @@ "@nestjs/websockets": "^9.4.0", "@prisma/client": "^4.12.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "socket.io-client": "^4.6.1" + "rxjs": "^7.2.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", @@ -51,6 +50,7 @@ "jest": "29.5.0", "prettier": "^2.3.2", "prisma": "^4.12.0", + "socket.io-client": "^4.6.1", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "29.0.5", From 23ec50cf5817a66fb1cbd8c30ad893b22328bb20 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 02:25:28 +0900 Subject: [PATCH 17/27] =?UTF-8?q?roomname=E3=81=AE=20unique=E3=82=92?= =?UTF-8?q?=E6=B6=88=E3=81=97=E3=81=9F=20unique=E3=81=A7=E3=81=AF=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7upsert?= =?UTF-8?q?=E3=81=8C=E4=BD=BF=E3=81=88=E3=81=AA=E3=81=84=20upsert->create?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/prisma/schema.prisma | 2 +- backend/prisma/seed.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index c436b958c..8a228cbf5 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -26,7 +26,7 @@ model ChatRoom { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - roomName String @unique + roomName String password String? isPrivate Boolean @default(false) isDM Boolean @default(false) diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts index 1b6d7b39f..6892a796e 100644 --- a/backend/prisma/seed.ts +++ b/backend/prisma/seed.ts @@ -25,12 +25,8 @@ async function main() { hashedPassword: 'piyopiyo', }, }); - const room = await prisma.chatRoom.upsert({ - where: { - roomName: 'hogeRoom', - }, - update: {}, - create: { + const room = await prisma.chatRoom.create({ + data: { roomName: 'hogeRoom', }, }); From 8248f33c3cafb9f461f635bfd5321029435b9f97 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 02:28:20 +0900 Subject: [PATCH 18/27] =?UTF-8?q?chat=20gateway=E3=81=AB=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E3=81=97=E3=81=9F=E5=85=A8=E5=93=A1=E3=81=AB?= =?UTF-8?q?room=E6=83=85=E5=A0=B1=E3=82=92=E8=BF=94=E3=81=99=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 2711a332a..9dafd1859 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -43,7 +43,6 @@ export class ChatGateway { }); client.join(createdRoom.id.toString()); this.server - .to(createdRoom.id.toString()) .emit('createChannel', createdRoom); } From 6dc6c196e7e2776e6eb4a87a1fc09546e84832bf Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 04:47:38 +0900 Subject: [PATCH 19/27] =?UTF-8?q?=E5=A4=A9=E6=89=8D=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=8B=E8=AA=AD=E3=82=81=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=A0=E3=81=A3=E3=81=9F=E3=81=AE?= =?UTF-8?q?=E3=81=A7=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=81=97?= =?UTF-8?q?=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 252 +++++++++++++++----------- 1 file changed, 151 insertions(+), 101 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index bcd80cb74..afc57b709 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { io, Socket } from 'socket.io-client'; -import { ChatRoom, Message, RoomMember, User } from '@prisma/client'; +import { ChatRoom, Message, Prisma, PrismaClient, RoomMember, User } from '@prisma/client'; import { IoAdapter } from '@nestjs/platform-socket.io'; import { INestApplication } from '@nestjs/common'; @@ -10,62 +10,103 @@ import { PrismaService } from '../prisma/prisma.service'; import { CreateChannelDto, JoinChannelDto } from './dto/Channel.dto'; import { ChatGateway } from './chat.gateway'; import { MessageDto } from './dto/message.dto'; - +type testUser = { + user: User; + socket: Socket; +} const USERNUM = 10; + +const createTestUsers = async (prismaService: PrismaService) => { + const testUsers: testUser[] = []; + for (let i = 0; i < USERNUM; i++) { + + const sock: Socket = io('http://localhost:8001'); + + const user:User = await prismaService.user.upsert({ + where: { + email: `chatTestUser${i}@test.com`, + }, + update: {}, + create: { + email: `chatTestUser${i}@test.com`, + username: `chatTestUser${i}`, + hashedPassword: `chatTestUser${i}`, + }, + }); + + testUsers.push({ user, socket: sock }); + } + return testUsers; +}; + +const cleanupDatabase = async (prisma:PrismaService): Promise => { + + const modelNames = Prisma.dmmf.datamodel.models.map((model) => model.name); + console.log(modelNames); + await Promise.all( + // @ts-ignore + // prisma. user prisma.chatroom 的なのになる + modelNames.map((modelName) => prisma[modelName.toLowerCase()].deleteMany()) + ); + + prisma.$disconnect(); +}; + +const emitAndWaitForEvent = async (eventName:string, socket:Socket, dto:T) => { + return new Promise((resolve) => { + socket.on(eventName, async () => resolve(null)); + socket.emit(eventName, dto); + }); +} + + + describe('ChatGateway', () => { let gateway: ChatGateway; let prismaService: PrismaService; - let clientSocket: Socket[]; - let users: User[]; + let testUsers: testUser[]; let room: ChatRoom | null; let app: INestApplication; + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [TestModule], providers: [ChatGateway, PrismaService], }).compile(); + gateway = module.get(ChatGateway); prismaService = module.get(PrismaService); + app = module.createNestApplication(); app.useWebSocketAdapter(new IoAdapter(app)); await app.listen(8001); - clientSocket = []; - users = []; - for (let i = 0; i < USERNUM; i++) { - const sock: Socket = io('http://localhost:8001'); - clientSocket.push(sock); - } - for (let i = 0; i < USERNUM; i++) { - const user = await prismaService.user.upsert({ - where: { - email: `chatTestUser${i}@test.com`, - }, - update: {}, - create: { - email: `chatTestUser${i}@test.com`, - username: `chatTestUser${i}`, - hashedPassword: `chatTestUser${i}`, - }, - }); - users.push(user); - } - for (let i = 0; i < USERNUM; i++) { - clientSocket[i].on('connect', () => { - console.log(`connected ${i}`); + + testUsers = []; + testUsers = await createTestUsers(prismaService); + + testUsers.map((testUser) => { + testUser.socket.on('connect', () => { + console.log(`connected ${testUser.user.username}`); }); - } + }); + }); afterAll(async () => { - for (let i = 0; i < USERNUM; i++) { - clientSocket[i].off('connect', () => { - console.log(`dissconnected ${i}`); + testUsers.map((testUser) => { + testUser.socket.off('connect', () => { + console.log(`dissconnected ${testUser.user.username}`); }); - } + }); + + testUsers.map((testUser) => { + testUser.socket.disconnect(); + }); + await app.close(); - for (let i = 0; i < USERNUM; i++) { - clientSocket[i].disconnect(); - } - await prismaService.chatRoom.delete({ + // await cleanupDatabase(prismaService); + + + await prismaService.chatRoom.deleteMany({ where: { roomName: room?.roomName || 'testroom', }, @@ -73,36 +114,38 @@ describe('ChatGateway', () => { for (let i = 0; i < USERNUM; i++) { await prismaService.user.delete({ where: { - id: users[i].id, + id: testUsers[i].user.id, }, }); } }); - it('should be defined', () => { + test('should be defined', () => { expect(gateway).toBeDefined(); }); + describe('create Channel', () => { - //users[0]が部屋を作成 - it('should be create Channel successfully', (done) => { + + test('users[0]が部屋を作成', async () => { + const user: testUser = testUsers[0]; const createChannelDto: CreateChannelDto = { roomName: 'testroom', - userId: users[0].id, + userId: user.user.id, }; - clientSocket[0].on('createChannel', async () => { - room = await prismaService.chatRoom.findFirst({ - where: { - roomName: 'testroom', - }, - }); - expect(room?.roomName).toEqual(createChannelDto.roomName); - done(); + await emitAndWaitForEvent('createChannel', user.socket, createChannelDto); + + room = await prismaService.chatRoom.findFirst({ + where: { + roomName: 'testroom', + }, }); - clientSocket[0].emit('createChannel', createChannelDto); + + expect(room?.roomName).toEqual(createChannelDto.roomName); }); }); describe('join Channel', () => { + let roomId: number; beforeEach(() => { if (!room) { @@ -110,37 +153,32 @@ describe('ChatGateway', () => { } roomId = room.id; }); - // users[1]~users[9]が部屋に参加 - it('should be join room successfully', async () => { - const promise = []; - let roomMemberList: RoomMember[] = []; - for (let i = 1; i < USERNUM; i++) { - const joinChannel: JoinChannelDto = { - userId: users[i].id, - chatRoomId: roomId, - }; - const joinPromise = new Promise((resolve) => { - clientSocket[i].on('joinChannel', async () => { - const newRoomMemberList = await prismaService.roomMember.findMany({ - where: { - chatRoomId: roomId, - }, - }); - roomMemberList = - roomMemberList.length < newRoomMemberList.length - ? newRoomMemberList - : roomMemberList; - resolve(null); + + test('users[1]~users[9]が部屋に参加', async () => { + const promises:Promise[] = []; + + testUsers.slice(1).map((testUser) => { + const joinChannel: JoinChannelDto = { + userId: testUser.user.id, + chatRoomId: roomId, + }; + const joinPromise = emitAndWaitForEvent('joinChannel', testUser.socket, joinChannel); + promises.push(joinPromise); + }); + + await Promise.all(promises).then( async () => { + const MemberList = await prismaService.roomMember.findMany({ + where: { + chatRoomId: roomId, + }, }); - clientSocket[i].emit('joinChannel', joinChannel); + expect(MemberList.length).toEqual(USERNUM); }); - promise.push(joinPromise); - } - await Promise.all(promise); - expect(roomMemberList.length).toEqual(USERNUM); - }); + }); }); + describe('sendMessage', () => { + let roomId: number; beforeEach(() => { if (!room) { @@ -148,42 +186,54 @@ describe('ChatGateway', () => { } roomId = room.id; }); - // users[0]が送信したメッセージがDBに保存されるか - it('stores user message in db successfully', (done) => { + + test('users[0]が送信したメッセージがDBに保存されるか', async () => { + const user: testUser = testUsers[0]; const messageDto: MessageDto = { content: 'test message', - userId: users[0].id, + userId: user.user.id, chatRoomId: roomId, }; - clientSocket[0].on('sendMessage', async () => { - const dbMsg = await prismaService.message.findFirst({ - where: { - chatRoomId: roomId, - }, - }); - expect(dbMsg?.content).toEqual(messageDto.content); - done(); + + await emitAndWaitForEvent('sendMessage', user.socket, messageDto); + const dbMsg = await prismaService.message.findFirst({ + where: { + chatRoomId: roomId, + }, }); - clientSocket[0].emit('sendMessage', messageDto); + + expect(dbMsg?.content).toEqual(messageDto.content); }); - // users[0]が送信したメッセージが全員に送信されるか - it('sends user message to all 1 in the room', (done) => { + + + test('users[0]が送信したメッセージが全員に送信されるか', async () => { + const user = testUsers[0]; const messageDto: MessageDto = { content: 'test message', - userId: users[0].id, + userId: user.user.id, chatRoomId: roomId, }; + const promises:Promise[] = [] + let receivedCount = 0; - for (let i = 0; i < USERNUM; i++) { - clientSocket[i].on('sendMessage', (emitMsg: Message) => { - expect(emitMsg.content).toEqual(messageDto.content); - receivedCount++; - if (receivedCount === USERNUM) { - done(); - } + + testUsers.map((user) => { + const joinPromise = new Promise((resolve) => { + user.socket.on('sendMessage', async () =>{ + receivedCount++; + console.log(receivedCount); + resolve(null); + }); }); - } - clientSocket[0].emit('sendMessage', messageDto); + + promises.push(joinPromise); + }); + + user.socket.emit('sendMessage', messageDto); + + Promise.all(promises).then(() => { + expect(receivedCount).toEqual(USERNUM); + }); }); }); }); From dfec4d54ae85e856d98c2fb788cdc5646e444c5c Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 21:49:08 +0900 Subject: [PATCH 20/27] =?UTF-8?q?test=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF?= =?UTF-8?q?=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 108 +++++++++++++------------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index afc57b709..eb73eacb7 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { io, Socket } from 'socket.io-client'; -import { ChatRoom, Message, Prisma, PrismaClient, RoomMember, User } from '@prisma/client'; +import { ChatRoom, User } from '@prisma/client'; import { IoAdapter } from '@nestjs/platform-socket.io'; import { INestApplication } from '@nestjs/common'; @@ -10,19 +10,20 @@ import { PrismaService } from '../prisma/prisma.service'; import { CreateChannelDto, JoinChannelDto } from './dto/Channel.dto'; import { ChatGateway } from './chat.gateway'; import { MessageDto } from './dto/message.dto'; + type testUser = { user: User; socket: Socket; -} +}; +const modelNames = ['chatRoom', 'user']; const USERNUM = 10; const createTestUsers = async (prismaService: PrismaService) => { const testUsers: testUser[] = []; for (let i = 0; i < USERNUM; i++) { - const sock: Socket = io('http://localhost:8001'); - const user:User = await prismaService.user.upsert({ + const user: User = await prismaService.user.upsert({ where: { email: `chatTestUser${i}@test.com`, }, @@ -39,27 +40,29 @@ const createTestUsers = async (prismaService: PrismaService) => { return testUsers; }; -const cleanupDatabase = async (prisma:PrismaService): Promise => { - - const modelNames = Prisma.dmmf.datamodel.models.map((model) => model.name); +const cleanupDatabase = async ( + modelNames: string[], + prisma: PrismaService, +): Promise => { console.log(modelNames); await Promise.all( - // @ts-ignore - // prisma. user prisma.chatroom 的なのになる - modelNames.map((modelName) => prisma[modelName.toLowerCase()].deleteMany()) + // prisma.user prisma.chatroom 的なのになる + modelNames.map( + async (modelName) => await (prisma as any)[modelName].deleteMany({}), + ), ); - - prisma.$disconnect(); }; -const emitAndWaitForEvent = async (eventName:string, socket:Socket, dto:T) => { +const emitAndWaitForEvent = async ( + eventName: string, + socket: Socket, + dto: T, +) => { return new Promise((resolve) => { socket.on(eventName, async () => resolve(null)); socket.emit(eventName, dto); }); -} - - +}; describe('ChatGateway', () => { let gateway: ChatGateway; @@ -89,7 +92,6 @@ describe('ChatGateway', () => { console.log(`connected ${testUser.user.username}`); }); }); - }); afterAll(async () => { testUsers.map((testUser) => { @@ -103,21 +105,7 @@ describe('ChatGateway', () => { }); await app.close(); - // await cleanupDatabase(prismaService); - - - await prismaService.chatRoom.deleteMany({ - where: { - roomName: room?.roomName || 'testroom', - }, - }); - for (let i = 0; i < USERNUM; i++) { - await prismaService.user.delete({ - where: { - id: testUsers[i].user.id, - }, - }); - } + await cleanupDatabase(modelNames, prismaService); }); test('should be defined', () => { @@ -125,14 +113,17 @@ describe('ChatGateway', () => { }); describe('create Channel', () => { - test('users[0]が部屋を作成', async () => { const user: testUser = testUsers[0]; const createChannelDto: CreateChannelDto = { roomName: 'testroom', userId: user.user.id, }; - await emitAndWaitForEvent('createChannel', user.socket, createChannelDto); + await emitAndWaitForEvent( + 'createChannel', + user.socket, + createChannelDto, + ); room = await prismaService.chatRoom.findFirst({ where: { @@ -145,7 +136,6 @@ describe('ChatGateway', () => { }); describe('join Channel', () => { - let roomId: number; beforeEach(() => { if (!room) { @@ -155,30 +145,33 @@ describe('ChatGateway', () => { }); test('users[1]~users[9]が部屋に参加', async () => { - const promises:Promise[] = []; + const promises: Promise[] = []; - testUsers.slice(1).map((testUser) => { - const joinChannel: JoinChannelDto = { - userId: testUser.user.id, - chatRoomId: roomId, - }; - const joinPromise = emitAndWaitForEvent('joinChannel', testUser.socket, joinChannel); - promises.push(joinPromise); - }); + testUsers.slice(1).map((testUser) => { + const joinChannel: JoinChannelDto = { + userId: testUser.user.id, + chatRoomId: roomId, + }; + const joinPromise = emitAndWaitForEvent( + 'joinChannel', + testUser.socket, + joinChannel, + ); + promises.push(joinPromise); + }); - await Promise.all(promises).then( async () => { - const MemberList = await prismaService.roomMember.findMany({ - where: { - chatRoomId: roomId, - }, - }); - expect(MemberList.length).toEqual(USERNUM); + await Promise.all(promises).then(async () => { + const MemberList = await prismaService.roomMember.findMany({ + where: { + chatRoomId: roomId, + }, }); + expect(MemberList.length).toEqual(USERNUM); }); + }); }); describe('sendMessage', () => { - let roomId: number; beforeEach(() => { if (!room) { @@ -195,7 +188,11 @@ describe('ChatGateway', () => { chatRoomId: roomId, }; - await emitAndWaitForEvent('sendMessage', user.socket, messageDto); + await emitAndWaitForEvent( + 'sendMessage', + user.socket, + messageDto, + ); const dbMsg = await prismaService.message.findFirst({ where: { chatRoomId: roomId, @@ -205,7 +202,6 @@ describe('ChatGateway', () => { expect(dbMsg?.content).toEqual(messageDto.content); }); - test('users[0]が送信したメッセージが全員に送信されるか', async () => { const user = testUsers[0]; const messageDto: MessageDto = { @@ -213,13 +209,13 @@ describe('ChatGateway', () => { userId: user.user.id, chatRoomId: roomId, }; - const promises:Promise[] = [] + const promises: Promise[] = []; let receivedCount = 0; testUsers.map((user) => { const joinPromise = new Promise((resolve) => { - user.socket.on('sendMessage', async () =>{ + user.socket.on('sendMessage', async () => { receivedCount++; console.log(receivedCount); resolve(null); From 294ea7325ad0c4244c655acfff883b3d336b0ce2 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 21:49:22 +0900 Subject: [PATCH 21/27] fmt --- backend/src/chat/chat.gateway.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 9dafd1859..9908ec52a 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -42,8 +42,7 @@ export class ChatGateway { }, }); client.join(createdRoom.id.toString()); - this.server - .emit('createChannel', createdRoom); + this.server.emit('createChannel', createdRoom); } @SubscribeMessage('joinChannel') From 35582a4b909f77f5520c233cd7bc08a98e67d07f Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 22:14:55 +0900 Subject: [PATCH 22/27] =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88?= =?UTF-8?q?=E3=81=9Amsg=E5=85=A8=E9=83=A8=E8=BF=94=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 9908ec52a..c04eff1aa 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -68,6 +68,11 @@ export class ChatGateway { chatRoomId: messageDto.chatRoomId, }, }); - this.server.to(msg.chatRoomId.toString()).emit('sendMessage', msg); + const roomMsgs = await this.prisma.message.findMany({ + where: { + chatRoomId: messageDto.chatRoomId, + }, + }); + this.server.to(msg.chatRoomId.toString()).emit('sendMessage', roomMsgs); } } From 94852aa6ba051059429ed1d7acf25f3eb7fea5a5 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 22:25:09 +0900 Subject: [PATCH 23/27] =?UTF-8?q?cleanupDatabase=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=81=8C=E6=B8=A1=E3=81=97=E3=81=9F=E5=BC=95=E6=95=B0=E3=81=AE?= =?UTF-8?q?=E9=A0=86=E9=80=9A=E3=82=8A=E3=81=AB=E5=AE=9F=E8=A1=8C=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9A=E3=81=AB=E3=82=A8=E3=83=A9=E3=83=BC=E5=87=BA?= =?UTF-8?q?=E3=81=A6=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index eb73eacb7..119b93f7a 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -15,6 +15,7 @@ type testUser = { user: User; socket: Socket; }; + const modelNames = ['chatRoom', 'user']; const USERNUM = 10; @@ -45,12 +46,10 @@ const cleanupDatabase = async ( prisma: PrismaService, ): Promise => { console.log(modelNames); - await Promise.all( - // prisma.user prisma.chatroom 的なのになる - modelNames.map( - async (modelName) => await (prisma as any)[modelName].deleteMany({}), - ), - ); + // prisma.user prisma.chatroom 的なのになる + modelNames.map(async (modelName) => { + await (prisma as any)[modelName].deleteMany({}); + }); }; const emitAndWaitForEvent = async ( From 8c2ddc525817dd8aac1a61f3dbf5ee1fd995910f Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Mon, 29 May 2023 22:25:09 +0900 Subject: [PATCH 24/27] =?UTF-8?q?cleanupDatabase=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=81=8C=E6=B8=A1=E3=81=97=E3=81=9F=E5=BC=95=E6=95=B0=E3=81=AE?= =?UTF-8?q?=E9=A0=86=E9=80=9A=E3=82=8A=E3=81=AB=E5=AE=9F=E8=A1=8C=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9A=E3=81=AB=E3=82=A8=E3=83=A9=E3=83=BC=E5=87=BA?= =?UTF-8?q?=E3=81=A6=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index eb73eacb7..506eb950e 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -15,6 +15,7 @@ type testUser = { user: User; socket: Socket; }; + const modelNames = ['chatRoom', 'user']; const USERNUM = 10; @@ -45,12 +46,10 @@ const cleanupDatabase = async ( prisma: PrismaService, ): Promise => { console.log(modelNames); - await Promise.all( - // prisma.user prisma.chatroom 的なのになる - modelNames.map( - async (modelName) => await (prisma as any)[modelName].deleteMany({}), - ), - ); + // prisma.user prisma.chatroom 的なのになる + for (const name of modelNames) { + await (prisma as any)[name].deleteMany({}); + } }; const emitAndWaitForEvent = async ( From 63dbde8bc85bcbce2fbab4deace7a5dc93a53e3a Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Tue, 30 May 2023 18:48:11 +0900 Subject: [PATCH 25/27] =?UTF-8?q?=E5=8F=AF=E8=AA=AD=E6=80=A7=E3=82=92?= =?UTF-8?q?=E4=B8=8A=E3=81=92=E3=81=9F=E3=80=80it->test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.controller.spec.ts | 2 +- backend/src/chat/chat.service.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/chat/chat.controller.spec.ts b/backend/src/chat/chat.controller.spec.ts index fb5699998..3f9e47fe3 100644 --- a/backend/src/chat/chat.controller.spec.ts +++ b/backend/src/chat/chat.controller.spec.ts @@ -13,7 +13,7 @@ describe('ChatController', () => { controller = module.get(ChatController); }); - it('should be defined', () => { + test('should be defined', () => { expect(controller).toBeDefined(); }); }); diff --git a/backend/src/chat/chat.service.spec.ts b/backend/src/chat/chat.service.spec.ts index 8783c8727..60e28ba74 100644 --- a/backend/src/chat/chat.service.spec.ts +++ b/backend/src/chat/chat.service.spec.ts @@ -13,7 +13,7 @@ describe('ChatService', () => { service = module.get(ChatService); }); - it('should be defined', () => { + test('should be defined', () => { expect(service).toBeDefined(); }); }); From 6ccfe1b50b87d681295c9353676b799da50b3364 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 31 May 2023 02:57:17 +0900 Subject: [PATCH 26/27] =?UTF-8?q?cuid=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=81=A7id:number->id:string=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/chat/chat.gateway.spec.ts | 4 ++-- backend/src/chat/dto/Channel.dto.ts | 6 +++--- backend/src/chat/dto/message.dto.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/chat/chat.gateway.spec.ts b/backend/src/chat/chat.gateway.spec.ts index 506eb950e..96743462b 100644 --- a/backend/src/chat/chat.gateway.spec.ts +++ b/backend/src/chat/chat.gateway.spec.ts @@ -135,7 +135,7 @@ describe('ChatGateway', () => { }); describe('join Channel', () => { - let roomId: number; + let roomId: string; beforeEach(() => { if (!room) { throw Error('room is not created'); @@ -171,7 +171,7 @@ describe('ChatGateway', () => { }); describe('sendMessage', () => { - let roomId: number; + let roomId: string; beforeEach(() => { if (!room) { throw Error('room is not created'); diff --git a/backend/src/chat/dto/Channel.dto.ts b/backend/src/chat/dto/Channel.dto.ts index d33a12be2..54c089588 100644 --- a/backend/src/chat/dto/Channel.dto.ts +++ b/backend/src/chat/dto/Channel.dto.ts @@ -1,8 +1,8 @@ export class JoinChannelDto { - chatRoomId: number; - userId: number; + chatRoomId: string; + userId: string; } export class CreateChannelDto { roomName: string; - userId: number; + userId: string; } diff --git a/backend/src/chat/dto/message.dto.ts b/backend/src/chat/dto/message.dto.ts index 36c673f54..3e4b884b2 100644 --- a/backend/src/chat/dto/message.dto.ts +++ b/backend/src/chat/dto/message.dto.ts @@ -1,5 +1,5 @@ export class MessageDto { content: string; - userId: number; - chatRoomId: number; + userId: string; + chatRoomId: string; } From b5184349a12928b47fbd8972fe3162f2c35da101 Mon Sep 17 00:00:00 2001 From: wyoheiii Date: Wed, 31 May 2023 02:57:30 +0900 Subject: [PATCH 27/27] sb-update --- .../app-page--select-channel.png | Bin 70685 -> 70684 bytes .../app-page--send-msg.png | Bin 73545 -> 73545 bytes .../app-page--send-some-msg.png | Bin 151249 -> 151249 bytes ...onents-elements-button-sendbutton--act.png | Bin 7345 -> 7350 bytes ...ents-elements-button-sendbutton--basic.png | Bin 7345 -> 7350 bytes ...tures-chat-components-chatinput--basic.png | Bin 7288 -> 7289 bytes ...es-chat-components-chattalkarea--basic.png | Bin 12651 -> 12651 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/src/app/__image_snapshots__/app-page--select-channel.png b/frontend/src/app/__image_snapshots__/app-page--select-channel.png index 10c8db5076e5f22b9dab834d49ab54316723760c..43effcd178e0383ed4068f31e60d170f2e4db7e2 100644 GIT binary patch delta 2518 zcmXX{3pkT)AKxl^>nhbJMUuDAYl=|ewU0Kv6>^$VsLUynoR4$fqvO8nQyEz=)4YWh zMQLFN^z!DAsF_(#iD7e?g*nYO-|d>`x}NK~?)!f3`~Um@?%)6S&wsIj{9;3THaruK zdryS3emT37Z}@24Nu~wmpt135ugEB83}&ip9ZdPqO!x2qL(H1RRE-L8Dt@V{Ed0-# zx^W46*49PB2-r8#=HqL_wB3Ys80oq?{0Q^}a&<9Jnh8YBH0ya<;5fxDpITbRUKU|3 z@pydC8~>X(Zr&V;q0^G*#=ragOS%8Y?adz|PCGifMMXtLnyyz)3b(`kES;Ym{;P_# z7A3BvsS;KtSe)ip^omF#Pahw=F)7;R!l7LmT4u#nBslmF0&m=Su4=md!+q``V%6v` zYHD4D4q4gR*@aJ@Xm*GFPVbNSPd-LNLt{)b#*q$ZxpsDR#B|eTGMTx5{RVK9U37Fb zVX48SD{=tHX=ZlTm@HXX;FXrwj&r7P%=aPOlGZX~ z1cb25Lb!Ci@o(6vuC74DBfoHtSO+3d>!H2Qn>cVVfWONjV^u%JxP|l%?&p@7(l=Z4xzNW+1o2M>Dp%#DKlyWGFzztV< zxpj4Qqo4nLzAUcuMP#*1JZ%fgY6~)}XV#=Fj0@S%b=uv}ocW;vLm&{2L^n4#$IrICK*$Pu(g=*N#AKQ%Xh zts1~XMm9D!`uh6U*w1V$y2FSj9vyNC)rK4U1w|bl4GJK4hjkJ4opx$!2Gh7ao{^s3 zNh&7aEjISu(9jUg@9%ZsD6ORf7Z4uj&nJ4@ z+uMf%^6jdHIObfNpRqN%q2aw1M3PswCt1>Yds?fFA=3KG!^ycE+yu3N)fV}<1 ze?!B=xdIAQ*45=vR8*uTSKXpn5Y2BK`{%B@omWguh3klKaxw!0QT(MABhoG=8zZL#P*f$fh%-3BJYA75!d{?BNx9{B9(S#cscl;rj5OFYM^{bD`YS9xLmY zmEm4tDg3)Jcn{jNq$ za5%5+-rc+GNBd8vN*P#_D?vfY?>{y+0#UiJ*=%4?NQA!r*j`#Enb3h6{idHF&}q^Zov(6HmaY3+-OiVB>-Dq)I@U+x`{ zo?n;ijG!!qgoeJ4pYWiBH@yo@>3 z@pG_2Lrd#rLBX$}@x3f5aZE}|vcgS10sJ#0@--C}(`dBzEHh;;Fw0kHri$iTVhbc2 hb$Am=R~>G#OAeVPbP=Pk4o$BvI@n@Pm7Var@qcOR?)?A& delta 2519 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ⅅ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 delta 10249 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 H!&^mUABfQ60{e~6SW;5lm57r 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 b_ugQ0BmA7(Q}WAtpsEfZI(m>3hr9ZJ=Cs@V delta 2388 zcmXw52Ut^C77j3qU_oUD3(b)oaYtEDM2bdqK80b`#6pJH6Rh z5bbsPl=qDiDG#XO$x)m+rRyYLQTS6dy}!TzQU761$Nhlceu>%XsAqY~(?QgIt_0CM z(*5pC!pOO#ZINvM&cd+Ze!Cwr0egtK0jHbq#f-wDmp&gM{(fp>as(8%!QOIe;%zuascq}kbXTVMG8P*mgA{aX>^SF_@$ z)CjVTzP=7}C{HwGmo=n{CDqzU5rSE2lg;#~`2PRcI`-oVGxnyPogNyGRM7(kc^LF0H`Z&}Wra}h{+ zvOVQIroMhh-~r@#?vGUaHS@S`z1d%g2y*Gywv1ODf4CeUz_d*n>X@;sI=+M5i706r zle!HCI*f$>XVaD@GjBT2>gdL;IvkdB9}BD(UVvFNPNxQLK8i(ra}rX!aBmmE9M$8{ z8ZZyXvR#?o7Bg#t&92L^Z}EJKaa#z|O*eh;7>y748?3;8aD=U;4}Tt3@{g_^NyGE& zcmwcFknK(no$7#DH+Y`Z);I*j_^9WPO`KOI01)^L1ZkC6*fl;iJ&qA6A0OpAUyx`p zsXXNkwB=MhQrQ4mEg3o4=nJRoJk%eZ&WcqeJi6;9NOvQG)-x`bqMa9q-t<}2f#5=) z6tvcS9rJ{9R#=NkYGuBkIEO?;11n{@VufJ}x5#P~KK&GXGxbXBXbR9eqv2CzT28Gy zs;I=B4ubhDloV@=syi)Kb@l*|8p|JXFJoPM_v;KzYS1Mvj4m3)6)3s1JZzMwfPK5y z2%;7aF_Sun_{#zl5j@&WPdIlBjfr%YyzyhcU}_%bN^q%gpFU>B^xK!#ZIf-IuOy2u zN)dDTPash6uwC^A9UQD({EL|$CcsyW?Xb$IW-4wmoamH`JM4>5EcHxwvewqVn!r*7 z$1VwOnQF!+`mS+3>jS;58BrJ2k_iL(?|E49!`eHx86glM7jwtGu#hIzxQZZ?)p@os zc6TVOcGdI=Sd>y8re^&ZF^t1(1w#FStrqX8_STeIdcjbvwZAVbwGR_{TyatxDjj!E z5MfQ!f_4>EQZzJ*Mu+%~GhgIe$x7x{!V`H{7R0o!39f>f6-X~@VN%mrU)mwp=c;$T zH0mtmYgEE78^z_>)EtJkfS?#sBpxJ4V z$piP&mj>b~c63$r0QHUpi?jp{TP&*DFYoKbNtAwmWXB9&zV`L(zj;evYHe@E9RX9U z-9i904<%m)%nP|4WYX?|b4-Tk`slvTe>RDp>`B{as2WS2%;694(adLb--A!(VpcX) z{OSeV2|!T(6?Bv{9|{FqIevdbCVh)Vy$LQk$c`vL$x!f?y5QtE9jp2D8dd0V`*MC0E2d}#HLWt|{9p4dHd+#um+`epe@ zJmZ2A7vob4l-IjOjx6{doT|&l1j!d?Dk9;EGWWk10yV6F3ZR54Y?dKMSJs@P(Q!!cxK6;jpQSr|W&H!y`S7A?eP(A9pFK6aPtmy;8n6;|N{+6JCEs2;} z>66(2Ml5rG{c0w?p`}^s>I+BZ(G->o;^G#PpS4!C@FpL9J!JSdLc}z1rD03{_5qDZ zLu{HW5cnG0BBozonXPiJC;Ob8O!cE)Y>N6g4Miqjh=;e_lu*VHSdH*B{FOYRmiIy8 zx>jjkhgHI1R_4=#^|)m`?Mv|wfIOSvJ19y+IVHTl>^{mxla@LZ>7ff*VI1unS&hZq zaX?Pwt3)i;5m6-8#AL|QWEwmc_jO)S63V) zSX}~5-DOkeRmtYcK}La;IDdu78pK8>r|KdcsfnNl`1s`I#GZln-cvnC*uZfpys_5F zMtm-$bFln|?e&Tw^G79e%(^n5YW(z~PPKOx8akLdk+F~&j6Jw*tyJ%MTx4tl9Ib4dR9wvSs3`7?Zk<2f-NDC4 zB=B}~UEG|_DqGp3fh-e1BRjnluKM|Aq_^z5=F-tEs1-*`s5G&W&AJ-_IBXNjo!$A) z{Ol^E3_W6Moatl3v88G%^FOVn#Q&TNEdTU#eV9(l#JAH>)G}l3HmZZMzTS>$t=`+` VD>mex!L{S>bp~;|?x)aS{|880#J>Ol diff --git a/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png b/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png index 95b1dc556c31a22b0674f34f609bb0b6be217e7d..b13c73f07ec1f1d1fd1737a771f12ab4cf3cd926 100644 GIT binary patch delta 2390 zcmX9<3sjPK8%L|TmPPx%tunuCt<&0B^VUW4N^>RaTPG?*Q$y&ZGD*>8h?nqXwdH21 zYi$-{vZnI_2pS?O2`n?$QpgJsD3_%ReL)cwNKp8A-FwdeoagyHm*4X|@Bcji)`<3q zA3}kg!v{Y*nO{An0oWQVrq^PRuxo9VaTkSyoZoi*cCNh4kGKBW%aHTlkyp!cPiVCL z_cD338#nY1XcHewsjWW*c0a63J^A{40$mo5uP=dYl8Rf0eGasm%l4_Q8&-LFkWJ6)y!Dfz1orxm*B{lM z`Z{}JDNh*a?F}VjuJ)1KZwa3zjk{@&hPZ|c_qhWXzN30fu$zQ9ubKG?B~id?F#hNm zs3t3JmG|s>VX7JQx>PSs!~&R*{9m+}W}8wJ;#%gx15i+W_%w!!d@9};nW(H>2tS#v ze8*y5yZeQSqvgEMKj&I&jq3#y?7#kL`Vw-#?oWnAV}u$`x{!O9*&5;{%foJSKt+et zuU;j&@0Z@3$STkKvrXHF*YtBq0#Qzi>2bVg4N7QhO1+qZ0wR`jTZDG+G`pwBn1oY5ts1 zbJ3vWXkF?Vh7)w{EHvg|FV>f=f>NLwli!6I^1{Fh4kj^tvF~w;B>^KRCN1IYmK1SQ zLqLJ;6dH}mFHT?5qjX`fBeeFObWltdp9puE9y>%;-7Vzy|$i^3r?X6H7ha#IuSh=El9 zFf&)vwRAgjC)C|dCHrkEdGzw}u3UKmY2>NKdKrc7XN<7n7;C*lT@u`6*DhV2y0S3d zghyO4=kl}6w6CUnfNR`dzIN=IwM6`|%{AF{&HDIv9#SAf%%7lGojwF&dTmhhZb7jV zbHn&G>U4_WinT*MFGhZ}BnYfl9^cwgc$hK{?yz!G;mSMVW-d~&l|Ekp{Tlu0zEW7e zRXu0S774fkBNoD@sGKV;v37}8-QhmFwq4y_PO$*T+$|sMueuk^U{5)Z4o{h}$HioG zZhDL@M~9n=7zV&1xLbG2!SkECEel@UrvNGezqfmADsxN zWH_4`{$)%G(g);>C=ZdPdg2!A&8;j$42ThThJ^65^v0y^npIorpjA55{Y+2~RDhT%}d ztgmESbDlh1v=FCp5A5Sxo@ogTyeyU9$l4$?H>TU}?KiXrPY@M>Nx=oAJl!l%z{jN_ zyYPqX$>Q9HT(mu=ShDVSi0}yA4^Cz2_BO=MiNcl5{$%CAzqwp{OyRF+MB(H?&+Q9s znnSooEe?Y_@)S@oRtg#s0`E6FmBCyCyqdkU22m^(8$5#o+l`g?hFXQ#k(J}0(Wy}K zkpA=0Fj0Ps7|?z-@W|nWH*-|d7 z{Mg&^Puj%!I8>HvOpxft32W5*qQSt4>Bhg;2$`zJ6y8~)=92YC$AX|5E4Kc;B|RF* zc@Ur}W-GEmOEm(b)`si}fT7m48%~@XL=PCP~uFn>J^{!ixVo&i6G`je{D7@nxhQ&g06>gF7Q+21U?fGi^U?INs-|{ zT5+4D=;LNz$?R`pknJ|*^?s*O)C)i2+^xfc9jwmIPNpg|k*8uanepB2A3_=XuKH0k zp!UKtwZPN9LlAa%wP($$wnA-`JM z{2rLh2o!ws^!Jq=%lWJO>%4Kb1C2K-qQlVmGu}b5vv*Yhc8@l#Md65%5x6; zN~BWhnd5|yUEfm^vOk{Px48^7nb;l>04F9I&+{Tod!RZu?fz?rWGYWs)t|I=+sn)_ zR&8zV>pofn`jzf-WX$~9Z^ieWAN-Sju&xru2=MAHMg31dUPj6);Vh}jmv`^Zk#APu zYf2>CZ(mF@V)z4EZ9gMqQz$-ofO1Z^-}^haA@$nEc(fMO&joF<4ju;$r%-Y@U$ b_ugQ0BmA7(Q}WAtpsEfZI(m>3hr9ZJ=Cs@V delta 2388 zcmXw52Ut^C77j3qU_oUD3(b)oaYtEDM2bdqK80b`#6pJH6Rh z5bbsPl=qDiDG#XO$x)m+rRyYLQTS6dy}!TzQU761$Nhlceu>%XsAqY~(?QgIt_0CM z(*5pC!pOO#ZINvM&cd+Ze!Cwr0egtK0jHbq#f-wDmp&gM{(fp>as(8%!QOIe;%zuascq}kbXTVMG8P*mgA{aX>^SF_@$ z)CjVTzP=7}C{HwGmo=n{CDqzU5rSE2lg;#~`2PRcI`-oVGxnyPogNyGRM7(kc^LF0H`Z&}Wra}h{+ zvOVQIroMhh-~r@#?vGUaHS@S`z1d%g2y*Gywv1ODf4CeUz_d*n>X@;sI=+M5i706r zle!HCI*f$>XVaD@GjBT2>gdL;IvkdB9}BD(UVvFNPNxQLK8i(ra}rX!aBmmE9M$8{ z8ZZyXvR#?o7Bg#t&92L^Z}EJKaa#z|O*eh;7>y748?3;8aD=U;4}Tt3@{g_^NyGE& zcmwcFknK(no$7#DH+Y`Z);I*j_^9WPO`KOI01)^L1ZkC6*fl;iJ&qA6A0OpAUyx`p zsXXNkwB=MhQrQ4mEg3o4=nJRoJk%eZ&WcqeJi6;9NOvQG)-x`bqMa9q-t<}2f#5=) z6tvcS9rJ{9R#=NkYGuBkIEO?;11n{@VufJ}x5#P~KK&GXGxbXBXbR9eqv2CzT28Gy zs;I=B4ubhDloV@=syi)Kb@l*|8p|JXFJoPM_v;KzYS1Mvj4m3)6)3s1JZzMwfPK5y z2%;7aF_Sun_{#zl5j@&WPdIlBjfr%YyzyhcU}_%bN^q%gpFU>B^xK!#ZIf-IuOy2u zN)dDTPash6uwC^A9UQD({EL|$CcsyW?Xb$IW-4wmoamH`JM4>5EcHxwvewqVn!r*7 z$1VwOnQF!+`mS+3>jS;58BrJ2k_iL(?|E49!`eHx86glM7jwtGu#hIzxQZZ?)p@os zc6TVOcGdI=Sd>y8re^&ZF^t1(1w#FStrqX8_STeIdcjbvwZAVbwGR_{TyatxDjj!E z5MfQ!f_4>EQZzJ*Mu+%~GhgIe$x7x{!V`H{7R0o!39f>f6-X~@VN%mrU)mwp=c;$T zH0mtmYgEE78^z_>)EtJkfS?#sBpxJ4V z$piP&mj>b~c63$r0QHUpi?jp{TP&*DFYoKbNtAwmWXB9&zV`L(zj;evYHe@E9RX9U z-9i904<%m)%nP|4WYX?|b4-Tk`slvTe>RDp>`B{as2WS2%;694(adLb--A!(VpcX) z{OSeV2|!T(6?Bv{9|{FqIevdbCVh)Vy$LQk$c`vL$x!f?y5QtE9jp2D8dd0V`*MC0E2d}#HLWt|{9p4dHd+#um+`epe@ zJmZ2A7vob4l-IjOjx6{doT|&l1j!d?Dk9;EGWWk10yV6F3ZR54Y?dKMSJs@P(Q!!cxK6;jpQSr|W&H!y`S7A?eP(A9pFK6aPtmy;8n6;|N{+6JCEs2;} z>66(2Ml5rG{c0w?p`}^s>I+BZ(G->o;^G#PpS4!C@FpL9J!JSdLc}z1rD03{_5qDZ zLu{HW5cnG0BBozonXPiJC;Ob8O!cE)Y>N6g4Miqjh=;e_lu*VHSdH*B{FOYRmiIy8 zx>jjkhgHI1R_4=#^|)m`?Mv|wfIOSvJ19y+IVHTl>^{mxla@LZ>7ff*VI1unS&hZq zaX?Pwt3)i;5m6-8#AL|QWEwmc_jO)S63V) zSX}~5-DOkeRmtYcK}La;IDdu78pK8>r|KdcsfnNl`1s`I#GZln-cvnC*uZfpys_5F zMtm-$bFln|?e&Tw^G79e%(^n5YW(z~PPKOx8akLdk+F~&j6Jw*tyJ%MTx4tl9Ib4dR9wvSs3`7?Zk<2f-NDC4 zB=B}~UEG|_DqGp3fh-e1BRjnluKM|Aq_^z5=F-tEs1-*`s5G&W&AJ-_IBXNjo!$A) z{Ol^E3_W6Moatl3v88G%^FOVn#Q&TNEdTU#eV9(l#JAH>)G}l3HmZZMzTS>$t=`+` VD>mex!L{S>bp~;|?x)aS{|880#J>Ol diff --git a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png index 3518ee0556395e754c6e5c119de2ab5472140b25..20b33ed79bbf410c630676847aac2e80775311a7 100644 GIT binary patch delta 2243 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 J`}Z?f{sUDnoR9zj diff --git a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png index 7922001da253716668c79110ffd06ab8a0db8ca0..066d76e4e0f0b8a7b16e7746aa3e661d5b68542d 100644 GIT binary patch delta 2175 zcmYLK2~?8l8YZh#O=Z(mddr2mS=mHssYGtjWR{splU_{5r5h@yhUNmU{5szBc*mtA zEjO%d>L>w<475{=K$$kL2zT2zHuE>py()c%)kfAg!VXR9jQPNEyP%>%D@!v=sbGr6e05w)7sWwGjnORxoC`x%x zup9dUNV|M~Jdx)b)K!?*)YKFm9sQge68MMu?(j%dQa0W<{}Q&2=W)`t+`D5TBULrC zR2p2iI2eZ|>vPxJn;v$f&;rWK_UJKh*qwzf8_UMP###|Us^ zGs%&lnn4k2U7G@skDk_BD>$5NyFX%5nbv6$wbcK+wZNi)(v&AowJxzu8Q2}D4kCma$ugSKc5lCPlQEOR3%>zHUg z!WkaHE5;+SH%^E*JOlZJ;n2gSMJNS!@LBx8dW3(IoaA(n3LSNPQTxN!tIyP)Fj0|y zH553ptYB9KF_m0bKV##L7E!vaOk8#)cMl7TGhG|{7nVr`!Ws+c0L~+KnkMnd&(Vwo zsus&emsdG#cl+>Bf!B;#C}H}(e`ess`J zk*Robi;8lF@=dF10I44+PFp+Z;0g3iF%N7xuZ;;5)agX9=#Y1)Wd$**!&aVGH8tI1 zNmIa#s?9OHLp>1I-o1J1N-jo@!l`=mt*PN}+%<`FLl;f!qrBmZ9|o+m*8s3aU80gY z)^%3lXGb8_sui`tOt!MKL+n4C@9U9?k4k8eWfU1s?}J%>V1*s(#uzCCjK`NMZDynQ ze#;DuX8eH(`xo*x>{6sSo!Pd{tE*jis7`W+M&fbg zUAx`^cIsU{UCo;_haEgSE{8uUrC5k;#cEo(!{-XrL>(GR3Fp_Var-8l|f<(Wc93=%9JoV1O&mu9G?PBqo#1RRcdi(RPqkB z(ORc561C#iz*wCFqF!1u+*p~PWq)}&^^{p$&~n5vzgib6tZ?dLS_};n5q^QBTF0AO|QZykZK-fqQ;}ceQbHG&Sa;w}4n+VLTN=}yY zqb8-V$TjF2U1Q_a8uZAhf}OhcTbaodm*5J5{i^^RftVBs;C#+JfD(KQHk)N7MvJcw zTaUR!PMRdsiE*NXgJ4?#uh?zBzMA1BTVP5*T!W~QcaK>eETO@{_Dh#6zc2m|3UX)W zef9f+cFv1FcFCAsi}2{zP^a+VDlBhjdadMsOWdc7?nF&rIA@;k^JE)&s3brp1vv<3 zTI3EgTEg;2la9!a$8VlP+sTAAS(7n^E|X2PI}#l#M}Pvv+qicb+@!1V)|GUK6peNi zKwIj9Oo8=VqxkIxU)yG{m;X2kp%z0TrSARxmPFI~Q?^Rj*DuVG3iaL>-(PWDNOM~;TO>PEX3WiqE9P1M2iyCCp6%!r;1)dNvwKiBBQ*3X_BWKyO zyWV)? zKcAh*DaXj?xgPN?6B1rSZ9sdjuyBuVDi{I}NJTfb?4jMe^z~<337VK9EEf)=&T;4n z#$>XSkd3l@a&T1k1`ptdfRXCfoq3?`Iz-ZI(Z|?v z_5CixF-+69aGQ_#x=l!*cjJ*d3s9`+>rB3Wdeir$LQ5CYQ}aDQv(fzA+&Qtog{j_; zIwRWE#fHz&cr+9}dJuMDur+%d|h{6pCfog<0gL3V6 z6^bIHfP??mtG|4)va>6E_;4>|la3m`>kqcEva-tU>FHTfC}yk65I809Xaf!6Ve+)L z)_SL*Md#4aO?@!@S_S|B delta 2181 zcmYLK3sjP68m7{<>|%GvCbdn?HZ?Cg@*T3g~%J^Q*^DHR) zbsygEm&&-1r}aPRqnv7kE|(;i#KHW-trCJ;{}E7i21PbJc+BH7yXPh4+c33Y;{dVP zLPzi0iJ5moGvq+K@Ua)qb#19m_*f4Df#{RWPe+n|w`>BJFYw}h`koJQjt)?0MMyna zDwWQEa}W&5+kL4z;N5d1^TXgEiN#_$R`%`5?65(#(R&97y$*uVB*oze0qBnoxX@%0-+Y~#NT!P7=CfI$+o?{z1-+=iopT^F=7vYM(dhW1_$4b%OBY`3?vzc;flWK5oT%t0zyM7)id|Qly_X z8cb!>9EXS$ttEgKOlcm7wh)hpM!$k>#c+fQgs zhR;gd>aINPFT0BLXaD`|)t#gR_8z1O%cDTqQ>%4Y+GQZNt|m3~YkJw!*^QgScP98q zRe_c4Zi4VPER+<0R_JIeOh%+lzZZ-(H#oKJ<1>{#Y8i!Q{NBHQU;#}jf$Rh$It$B# z(7m*=hSR3&jV(wh<6K{i>{Vx+Nr-zHEz#7Qn7!hfCVy*5hl>c@+PfJ=pLP6>PYrvyJ^n45r0_lV5G$_F%k9C6>> z;`>e;II;>ecXyOLM@1RV!rWnx8oyP>PWo?LD^dmThin5umDw8zURn>?EWHzc+8fmy zZqv>jNgLW*3J6gcj?EH^X#q1RwD2bID_hpAQ2osv1Mm%bRpg((drWvLAk}<}jg2;GPD1BynTA!27~t>*^a_%8 z-7HwhirWaMj-@}XF47y-(cW5~5i{!rxhQOcW@Rgaiq*H0;j7Prr$C|s*_$|9A!35# zygQaQ5z-{}lr_dGXE6d7*f=GL)EYBr)gzm&QO1bttTY$I^ma*0uNo#_?xDX01elD? z&t^U$EZmDDdEhA!ZW)dPdw3pmmWPn!#9mmcjh^l=*2MLuPzCTu_lK;BqWO3p%a#tE zUf1@flzJQvh}%q)jQaDFx?8roePrdgmY|HaL}NI$pTM%F43&u01kTi&N26y4J9oKo ziScELE8C*Wy%vxv&D3+;W{L!0ySi%@N15wQhBKQz7|U`E z41&O}rMMbwi3IRF>V`2*i4EGXL3p!?ZP~NX$IiZUyU3JR%MtVcZWz%o-W1sK!l@I8-|igR zzimHQIu9_CzMsMh8lS_qQig(%@|Ez+MN^-~4`dXBLPF8YDF{GRTGOADkboP%c22nu zAG>JXMRyzE3OTB)9jQZ%#~Hel8o^aESL_RBC6s0g1uEOCI26icwM&9lS~IHncS*9sUyivK z{u$ZWzt;*YA4f2p6_?%?fO89E%7oec*qBO@bk3>J1_#+ew1?w_Xre1-=)Y`4XwXdtE zfvIY*4Kl3$ejW|Z(+_TL74b=S*YSzm&-Y7-NM?