Skip to content

Commit 172a42a

Browse files
authored
Merge pull request #370 from MangoInstantMessenger/azure_for_students
Azure for students
2 parents a801197 + 2b11acf commit 172a42a

43 files changed

Lines changed: 366 additions & 222 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-test-coverage-dotnet.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ jobs:
8282
CoverletOutput: TestResults/
8383
# dotnet test MangoAPI.IntegrationTests/MangoAPI.IntegrationTests.csproj -p:CollectCoverage=true -p:CoverletOutput=TestResults/ -p:CoverletOutputFormat=lcov
8484

85-
- name: Publish coverage report to Coveralls
86-
uses: coverallsapp/github-action@master
87-
with:
88-
github-token: ${{ secrets.GITHUB_TOKEN }}
89-
path-to-lcov: 'MangoAPI.IntegrationTests/TestResults/coverage.info'
85+
# - name: Publish coverage report to Coveralls
86+
# uses: coverallsapp/github-action@master
87+
# with:
88+
# github-token: ${{ secrets.GITHUB_TOKEN }}
89+
# path-to-lcov: 'MangoAPI.IntegrationTests/TestResults/coverage.info'
9090

9191
- name: Publish .NET Project
9292
run: |

MangoAPI.BusinessLogic/ApiCommands/Messages/SendMessageCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace MangoAPI.BusinessLogic.ApiCommands.Messages;
77

88
public record SendMessageCommand(
9+
Guid MessageId,
910
Guid UserId,
1011
Guid ChatId,
1112
string Text,

MangoAPI.BusinessLogic/ApiCommands/Messages/SendMessageCommandHandler.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
using System.Threading.Tasks;
55
using MangoAPI.Application.Interfaces;
66
using MangoAPI.Application.Services;
7+
using MangoAPI.BusinessLogic.HubConfig;
78
using MangoAPI.BusinessLogic.Models;
89
using MangoAPI.BusinessLogic.Responses;
910
using MangoAPI.Domain.Constants;
1011
using MangoAPI.Domain.Entities;
1112
using MangoAPI.Infrastructure.Database;
1213
using MediatR;
14+
using Microsoft.AspNetCore.SignalR;
1315
using Microsoft.EntityFrameworkCore;
1416

1517
namespace MangoAPI.BusinessLogic.ApiCommands.Messages;
@@ -21,17 +23,19 @@ public class SendMessageCommandHandler
2123
private readonly ResponseFactory<SendMessageResponse> responseFactory;
2224
private readonly IBlobServiceSettings blobServiceSettings;
2325
private readonly IBlobService blobService;
26+
private readonly IHubContext<ChatHub, IHubClient> hubContext;
2427

2528
public SendMessageCommandHandler(
2629
MangoDbContext dbContext,
2730
ResponseFactory<SendMessageResponse> responseFactory,
2831
IBlobServiceSettings blobServiceSettings,
29-
IBlobService blobService)
32+
IBlobService blobService, IHubContext<ChatHub, IHubClient> hubContext)
3033
{
3134
this.dbContext = dbContext;
3235
this.responseFactory = responseFactory;
3336
this.blobServiceSettings = blobServiceSettings;
3437
this.blobService = blobService;
38+
this.hubContext = hubContext;
3539
}
3640

3741
public async Task<Result<SendMessageResponse>> Handle(
@@ -69,6 +73,7 @@ public async Task<Result<SendMessageResponse>> Handle(
6973
var attachmentUniqueFileName = await UploadAttachmentIfExistsAsync(request);
7074

7175
var messageEntity = MessageEntity.Create(
76+
request.MessageId,
7277
request.UserId,
7378
request.ChatId,
7479
request.Text,
@@ -92,15 +97,18 @@ public async Task<Result<SendMessageResponse>> Handle(
9297
var attachmentUrl = attachmentUniqueFileName == null
9398
? null
9499
: $"{blobServiceSettings.MangoBlobAccess}/{attachmentUniqueFileName}";
95-
100+
96101
var messageResultDto = messageEntity.ToMessage(
97102
user.DisplayName,
98103
user.Id,
99104
user.DisplayNameColour,
100105
authorPictureUrl,
101106
attachmentUrl);
102107

103-
return responseFactory.SuccessResponse(SendMessageResponse.FromSuccess(messageResultDto));
108+
await hubContext.Clients.Group(request.ChatId.ToString()).BroadcastMessageAsync(messageResultDto);
109+
110+
return responseFactory.SuccessResponse(SendMessageResponse.FromSuccess(messageEntity.Id, attachmentUrl,
111+
messageEntity.CreatedAt));
104112
}
105113

106114
private async Task<string> UploadAttachmentIfExistsAsync(SendMessageCommand request)

MangoAPI.BusinessLogic/ApiCommands/Messages/SendMessageRequest.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@ namespace MangoAPI.BusinessLogic.ApiCommands.Messages;
66

77
public class SendMessageRequest
88
{
9-
[DefaultValue("hello world")]
10-
public string Text { get; set; }
9+
[DefaultValue("hello world")] public string Text { get; set; }
1110

1211
[DefaultValue("a8747c37-c5ef-4a87-943c-3ee3ae0a2871")]
1312
public Guid ChatId { get; set; }
1413

15-
[DefaultValue("John Doe")]
16-
public string InReplyToUser { get; set; }
14+
[DefaultValue("John Doe")] public string InReplyToUser { get; set; }
1715

18-
[DefaultValue("Hello world!")]
19-
public string InReplyToText { get; set; }
16+
[DefaultValue("Hello world!")] public string InReplyToText { get; set; }
2017

2118
[DefaultValue("2021-08-01T00:00:00.0000000")]
2219
public DateTime? CreatedAt { get; set; }
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
using MangoAPI.BusinessLogic.Models;
2-
using MangoAPI.BusinessLogic.Responses;
1+
using MangoAPI.BusinessLogic.Responses;
32
using MangoAPI.Domain.Constants;
3+
using System;
44

55
namespace MangoAPI.BusinessLogic.ApiCommands.Messages;
66

77
public record SendMessageResponse : ResponseBase
88
{
9-
public Message MessageModel { get; init; }
9+
public string AttachmentUrl { get; set; }
1010

11-
public static SendMessageResponse FromSuccess(Message messageModel)
11+
public Guid NewMessageId { get; set; }
12+
13+
public DateTime CreatedAt { get; set; }
14+
15+
public static SendMessageResponse FromSuccess(Guid newMessageId, string attachmentUrl, DateTime createdAt)
1216
{
1317
return new SendMessageResponse
1418
{
1519
Success = true,
1620
Message = ResponseMessageCodes.Success,
17-
MessageModel = messageModel,
21+
NewMessageId = newMessageId,
22+
AttachmentUrl = attachmentUrl,
23+
CreatedAt = createdAt
1824
};
1925
}
2026
}

MangoAPI.Client/src/app/components/chats/chats.component.ts

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { SendMessageResponse } from '../../types/responses/SendMessageResponse';
2323
import { ReplyStateService } from 'src/app/services/states/replyState.service';
2424
import { Reply } from 'src/app/types/models/Reply';
2525
import { GetChatMessagesResponse } from '../../types/responses/GetChatMessagesResponse';
26-
import { RealtimeService } from '../../services/api/realtime.service';
2726
import { BaseResponse } from '../../types/responses/BaseResponse';
2827
import { GetUserChatsResponse } from '../../types/responses/GetUserChatsResponse';
2928
import { DeleteMessageResponse } from '../../types/responses/DeleteMessageResponse';
@@ -46,7 +45,7 @@ export class ChatsComponent implements OnInit {
4645
private _apiBaseService: ApiBaseService,
4746
public _modalWindowStateService: ModalWindowStateService,
4847
public _replyStateService: ReplyStateService,
49-
private _realtimeService: RealtimeService,
48+
// private _realtimeService: RealtimeService,
5049
private _defaultChatHelper: DefaultChatHelper
5150
) {}
5251

@@ -153,8 +152,11 @@ export class ChatsComponent implements OnInit {
153152
});
154153

155154
this.connection.on('PrivateChatDeletedAsync', (chatId: string) => {
156-
console.log(`Private chat deleted: ${chatId}`);
157-
this.chats = this.chats.filter((x) => x.chatId !== chatId);
155+
const chatIndex = this.chats.findIndex((x) => x.chatId === chatId);
156+
157+
if (chatIndex === -1) return;
158+
159+
this.chats.splice(chatIndex, 1);
158160

159161
if (this.activeChatId === chatId) {
160162
this.activeChat = this._defaultChatHelper.getEmptyChat();
@@ -191,7 +193,6 @@ export class ChatsComponent implements OnInit {
191193

192194
this.connection.invoke('JoinGroup', this.activeChat.chatId).then(() => {
193195
this.realTimeConnections.push(this.activeChat.chatId);
194-
console.log(`SignalR JoinGroup: ${this.activeChat.chatId}`);
195196
});
196197
}
197198

@@ -227,10 +228,12 @@ export class ChatsComponent implements OnInit {
227228
return;
228229
}
229230

231+
const messageId = crypto.randomUUID();
230232
const createdAt = new Date().toISOString();
231233
const sendMessageFormData = new FormData();
232234

233235
sendMessageFormData.append('text', newMessageText);
236+
sendMessageFormData.append('messageId', messageId);
234237
sendMessageFormData.append('chatId', this.activeChatId);
235238
sendMessageFormData.append('inReplyToUser', this._replyStateService.reply?.displayName ?? '');
236239
sendMessageFormData.append('inReplyToText', this._replyStateService.reply?.text ?? '');
@@ -240,6 +243,7 @@ export class ChatsComponent implements OnInit {
240243
}
241244

242245
const newMessage = new Message(
246+
messageId,
243247
tokens.userId,
244248
this.activeChatId,
245249
tokens.userDisplayName,
@@ -256,24 +260,34 @@ export class ChatsComponent implements OnInit {
256260

257261
this.messages.push(newMessage);
258262

263+
this.pushChatToTop(this.activeChatId);
264+
259265
this._replyStateService.setReplyNull();
260266

267+
this.scrollToEnd();
268+
261269
const sendMessage$ = this._messagesService.sendMessage(sendMessageFormData);
262270

263271
const response = await firstValueFrom<SendMessageResponse>(sendMessage$);
264272

265-
newMessage.messageId = response.messageModel.messageId;
266-
newMessage.attachmentUrl = response.messageModel.attachmentUrl;
267-
newMessage.createdAt = response.messageModel.createdAt;
273+
newMessage.attachmentUrl = response.attachmentUrl;
274+
newMessage.createdAt = response.createdAt;
268275

269-
const sendNotification$ = this._realtimeService.sendRealtimeNewMessageNotification(
270-
response.messageModel
271-
);
276+
if (this.messageAttachment) {
277+
this.clearAttachmentInput();
278+
}
279+
}
272280

273-
await firstValueFrom<BaseResponse>(sendNotification$);
281+
pushChatToTop(chatId: string) {
282+
const chatIndex = this.chats.findIndex((x) => x.chatId === chatId);
274283

275-
this.clearAttachmentInput();
276-
this.scrollToEnd();
284+
if (chatIndex === -1) return;
285+
286+
const chat = this.chats[chatIndex];
287+
288+
this.chats.splice(chatIndex, 1);
289+
290+
this.chats.splice(0, 0, chat);
277291
}
278292

279293
onReplyClick(
@@ -294,15 +308,16 @@ export class ChatsComponent implements OnInit {
294308
}
295309

296310
async deleteMessage(message: Message) {
297-
const deleteMessageCommand: DeleteMessageCommand = {
298-
chatId: message.chatId,
299-
messageId: message.messageId
300-
};
311+
const deleteMessageCommand = new DeleteMessageCommand(message.messageId, message.chatId);
301312

302-
const deleteMessageSub$ = this._messagesService.deleteMessage(deleteMessageCommand);
303-
const deleteMessageResult = await firstValueFrom<DeleteMessageResponse>(deleteMessageSub$);
313+
const messageIndex = this.messages.findIndex((x) => x.messageId === message.messageId);
314+
315+
if (messageIndex === -1) return;
304316

305-
console.log(deleteMessageResult.message);
317+
this.messages.splice(messageIndex, 1);
318+
319+
const deleteMessageSub$ = this._messagesService.deleteMessage(deleteMessageCommand);
320+
await firstValueFrom<DeleteMessageResponse>(deleteMessageSub$);
306321
}
307322

308323
async onJoinChatClick() {
@@ -351,13 +366,9 @@ export class ChatsComponent implements OnInit {
351366
const div = event.currentTarget as HTMLDivElement;
352367
this.chatFilter = div.innerText;
353368

354-
console.log(this.chatFilter);
355-
356369
const getChatsSub$ = this._communitiesService.getUserChats();
357370
const getChatsResult = await firstValueFrom<GetUserChatsResponse>(getChatsSub$);
358371

359-
console.log(getChatsResult.chats);
360-
361372
switch (this.chatFilter) {
362373
case 'All chats':
363374
this.chats = getChatsResult.chats.filter((x) => !x.isArchived);
@@ -380,9 +391,7 @@ export class ChatsComponent implements OnInit {
380391

381392
async onLeaveChatClick() {
382393
const leaveChatSub$ = this._userChatsService.leaveCommunity(this.activeChatId);
383-
const leaveChatResult = await firstValueFrom<BaseResponse>(leaveChatSub$);
384-
385-
console.log(leaveChatResult.message);
394+
await firstValueFrom<BaseResponse>(leaveChatSub$);
386395

387396
this.activeChatId = '';
388397

@@ -391,9 +400,7 @@ export class ChatsComponent implements OnInit {
391400

392401
async onArchiveChatClick() {
393402
const archiveChatSub$ = this._userChatsService.archiveCommunity(this.activeChatId);
394-
const archiveResult = await firstValueFrom<BaseResponse>(archiveChatSub$);
395-
396-
console.log(archiveResult.message);
403+
await firstValueFrom<BaseResponse>(archiveChatSub$);
397404

398405
this.activeChatId = '';
399406

@@ -402,21 +409,27 @@ export class ChatsComponent implements OnInit {
402409

403410
private onMessageSendHandler(message: Message) {
404411
message.self = message.userId == this.userId;
405-
const chat = this.chats.filter((x) => x.chatId === message.chatId)[0];
412+
413+
const chatIndex = this.chats.findIndex((x) => x.chatId === message.chatId);
414+
415+
if (chatIndex === -1) return;
416+
const chat = this.chats[chatIndex];
417+
406418
chat.lastMessageAuthor = message.userDisplayName;
407419
chat.lastMessageText = message.text;
408420
chat.lastMessageTime = message.createdAt;
409421
chat.lastMessageId = message.messageId;
410-
this.chats = this.chats.filter((x) => x.chatId !== message.chatId);
411-
this.chats = [chat, ...this.chats];
412422

413-
const includesMessage = this.messages.some((x) => x.messageId === message.messageId);
423+
this.chats.splice(chatIndex, 1);
424+
this.chats.splice(0, 0, chat);
425+
426+
if (this.activeChatId !== message.chatId) return;
427+
428+
const messageIndex = this.messages.findIndex((x) => x.messageId === message.messageId);
414429

415-
if (message.chatId === this.activeChatId && !includesMessage) {
430+
if (messageIndex === -1) {
416431
this.messages.push(message);
417432
}
418-
419-
this.scrollToEnd();
420433
}
421434

422435
private onMessageEditHandler(notification: EditMessageNotification) {
@@ -434,25 +447,25 @@ export class ChatsComponent implements OnInit {
434447
}
435448

436449
private onMessageDeleteHandler(notification: DeleteMessageNotification) {
437-
console.log(notification);
450+
const chatIndex = this.chats.findIndex((x) => x.chatId === notification.chatId);
438451

439-
// const message = this.messages.filter((x) => x.messageId === notification.deletedMessageId)[0];
452+
if (notification.isLastMessage && chatIndex !== -1) {
453+
const updatedChat = this.chats[chatIndex];
440454

441-
console.log(`deleted message id: ${notification.deletedMessageId}`);
442-
console.log(`last message id: ${this.activeChat.lastMessageId}`);
443-
444-
if (notification.isLastMessage) {
445-
const updatedChat = this.chats.filter((x) => x.chatId == notification.chatId)[0];
446455
updatedChat.lastMessageAuthor = notification.newLastMessageAuthor;
447456
updatedChat.lastMessageText = notification.newLastMessageText;
448457
updatedChat.lastMessageTime = notification.newLastMessageTime;
449458
updatedChat.lastMessageId = notification.newLastMessageId;
450-
451-
console.log(`updated chat: ${JSON.stringify(updatedChat)}`);
452459
}
453460

454-
if (this.activeChatId === notification.chatId) {
455-
this.messages = this.messages.filter((x) => x.messageId !== notification.deletedMessageId);
461+
if (this.activeChatId !== notification.chatId) return;
462+
463+
const messageIndex = this.messages.findIndex(
464+
(x) => x.messageId === notification.deletedMessageId
465+
);
466+
467+
if (messageIndex !== -1) {
468+
this.messages.splice(messageIndex, 1);
456469
}
457470
}
458471

0 commit comments

Comments
 (0)