I have a chat application with SignalR in Net5 and @microsoft/signalr in Angular12. The problem I have is when the connection is lost, i need to reconnect and rejoin a group, the reconnection does work but the reconnection to a group does not.
The function I use to join a group in signalr.service.ts is:
addTogroup(room: any,user:any)
The method I use to recover the connection in signalr.service.ts is:
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
Is it possible to access in any way the function that joins you to a group, when the withAutomaticReconnect is being executed? To join the group you need the user and the room that are in app.component.ts
Thank you very much for the help, I have already read the documentation many times but I cannot find the solution.
My code is:
app.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { SignalrService } from '../services/signalr.service';
import { HttpClient } from '@angular/common/http';
import { i18nMetaToJSDoc } from '@angular/compiler/src/render3/view/i18n/meta';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('myinputfile') myinputfile: any;
title = 'chat-ui';
listMessages: any[] = [];
messages: string[] = [];
message: string = "";
user: string = "";
ticket: string = "";
currentFiles: any = [];
myimg: any = "";
constructor(public signalrService: SignalrService, private http: HttpClient) {
}
ngOnInit(): void {
this.signalrService.startConnection((message: any) => {
this.listMessages.push({ mensaje: message.mensaje, user: message.user, tipo: "texto" });
}, (imageMessage: any) => {
this.listMessages.push(imageMessage);
}, (incomingConnection: any) => {
console.log(incomingConnection);
}, (error:any) => {
console.log(error);
});
}
addRoom() {
this.signalrService.addTogroup(this.ticket, this.user);
}
async sendMessage() {
if (this.myinputfile.nativeElement.files.length > 0) {
let formData = new FormData();
formData.append("RoomId", this.ticket);
formData.append("IdUsuario", this.user);
formData.append("IdCliente", "1");
formData.append("Interno", "1");
formData.append("File", this.myinputfile.nativeElement.files[0]);
await this.signalrService.sendImagesMessage(formData, (response: any) => {
this.myimg = response;
}, (error: any) => {
console.log(error);
});
this.myinputfile.nativeElement.value = "";
} else {
this.signalrService.sendMessageGroup(this.ticket, this.user, this.message);
}
}
onaddremoveFiles() {
if (this.myinputfile.nativeElement.files.length == 0) {
this.myinputfile.nativeElement.click();
} else {
this.myinputfile.nativeElement.value = "";
}
}
onfilesSelected(files: any) { return files.length > 0; }
openImages(src: any) {
let data = src;
let w = window.open('about:blank');
let image = new Image();
image.src = data;
if (w !== null) {
w.document.write(image.outerHTML);
}
}
}
signalr.service.ts
import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr";
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class SignalrService {
public hubConnection!: signalR.HubConnection;
constructor(private http:HttpClient) {
this.buildConnection();
//this.startConnection();
}
public buildConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Information)
.withUrl("https://localhost:44352/chatHub",{
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
})
//.withAutomaticReconnect([0,1000,5000,6000,7000,8000,10000,15000])
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// If we've been reconnecting for less than 60 seconds so far,
// wait between 0 and 10 seconds before the next reconnect attempt.
return Math.random() * 10000;
} else {
// If we've been reconnecting for more than 60 seconds so far, stop reconnecting.
return null;
}
}
})
.build();
};
public startConnection = (
onMessageCallback:Function,
onMessageImageCallback:Function,
onIncomingConnectionCallback:Function,
onErrorCallback:Function) => {
this.hubConnection
.start()
.then(() => {
console.log("Connection Started...");
this.ListeningConnections();
this.ListeningIncomeMessages(onMessageCallback);
this.ListeningIncomeImagesMessages(onMessageImageCallback);
this.ListeningIncomingConnection(onIncomingConnectionCallback);
this.ListeningError(onErrorCallback);
})
.catch(err => {
/*console.log("Error while starting connection: " + err);
setTimeout(() => {
this.startConnection();
}, 3000);*/
});
};
private ListeningConnections(){
this.hubConnection.on("ReceiveConnID", function (connid) {
console.log("ConnID: " + connid);
});
}
public addTogroup(room: any,user:any){
this.hubConnection.invoke("AddToGroup", room, user);
}
public sendMessageGroup(room: any,user:any,message:any){
this.hubConnection.invoke("SendMessageGroup",room, user,message,0)
.catch(function (err) {
return console.error(err.toString());
});
}
public ListeningIncomeMessages(onMessageCallback:Function){
this.hubConnection.on("ReceiveMessageGroup", (user, message) => {
onMessageCallback({mensaje: message, user: user});
});
}
public ListeningIncomingConnection(onIncomingConnectionCallback:Function){
this.hubConnection.on("IncomingConnection",(message) => {
onIncomingConnectionCallback({mensaje: message});
});
}
public ListeningUserConnected(onMessageCallback:Function){
this.hubConnection.on("ReceiveMessageUserConnected", (user, message) => {
onMessageCallback({mensaje: message, user: user});
});
}
public ListeningError(onErrorCallback:Function){
this.hubConnection.on("onError", (message) => {
onErrorCallback({mensaje: message});
});
}
public ListeningIncomeImagesMessages(onImageMessageCallback:Function){
this.hubConnection.on("ReceiveImageMessageGroup", (user, image) => {
//console.log(image);
// console.log(image.idUsuario);
// console.log(image.idCliente);
onImageMessageCallback({ mensaje: image, user: user, tipo: "imagen" });
});
}
public async sendImagesMessage(formData:FormData,onOkCallback:Function,onErroCallback:Function){
await this.http.post("https://localhost:44352/UploadImages",formData).toPromise().then((response)=>{
onOkCallback(response);
}).catch((error)=>{
onErroCallback(error);
console.log(error);
});
}
}
ChatHub.cs
public class ChatHub : Hub
{
public readonly static List<UserViewModel> _Connections = new List<UserViewModel>();
public readonly static List<RoomViewModel> _Rooms = new List<RoomViewModel>();
private readonly static Dictionary<string, string> _ConnectionsMap = new Dictionary<string, string>();
private readonly TicketingContext _context;
private readonly IMapper _mapper;
private readonly IMensajeService _mensajeService;
private readonly IUnitOfWork _unitOfWork;
public ChatHub(TicketingContext context, IUnitOfWork unitOfWork, IMensajeService mensajeService, IMapper mapper)
{
_context = context;
_mensajeService = mensajeService;
_unitOfWork = unitOfWork;
_mapper = mapper;
}
public IEnumerable<RoomViewModel> GetRooms()
{
if (_Rooms.Count == 0)
{
foreach (var room in _context.Tickets)
{
var roomViewModel = _mapper.Map<Tickets, RoomViewModel>(room);
_Rooms.Add(roomViewModel);
}
}
return _Rooms.ToList();
}
public IEnumerable<UserViewModel> GetUsers(string roomName)
{
return _Connections.Where(u => u.CurrentRoom == roomName).ToList();
}
//envia mensajes a un grupo determinado
public async Task SendMessageGroup(string room, string user, string message, int interno)
{
try
{
var msg = new MensajeGrabarDto()
{
IdTicket = Int32.Parse(room),
IdUsuario = Int32.Parse(user),
Texto = Regex.Replace(message, @"(?i)<(?!img|a|/a|/img).*?>", string.Empty),
Interno = interno,
Adjunto = new byte[] { },
AdjuntoNombre = "",
AdjuntoTipo = ""
//llenar los campos
};
DbResponse respon = await _unitOfWork.MensajeRepository.InsertarMensajeRepository(msg);
if (respon.retcode == 0)
{
await _unitOfWork.SaveChangesAsync();
var mensaje = await _mensajeService.ObtenerMensajesPorTicketService(Int32.Parse(room));
var mensaje2 = mensaje.Where(x => x.IdTicket == Int32.Parse(room)).OrderByDescending(x => x.IdMensaje).FirstOrDefault();
await Clients.Group(room).SendAsync("ReceiveMessageGroup", user, mensaje2);
}
else {
await Clients.Caller.SendAsync("onError", respon.mensaje);
}
}
catch (Exception)
{
await Clients.Caller.SendAsync("onError", "Message not send! Message should be 1-1500 characters.");
}
}
//une a un usuario a determinado grupo
public async Task AddToGroup(string room, string usuario)
{
try
{
await Groups.AddToGroupAsync(Context.ConnectionId, room);
//mensaje para decir que alguien se conecto
await Clients.Group(room).SendAsync("IncomingConnection", $"Alguien se conecto { usuario}");
}
catch (Exception ex)
{
await Clients.Caller.SendAsync("onError", "You failed to join the chat room!" + ex.Message);
}
}
//desloguea a un usuario de un grupo
public async Task Leave(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
}
public override Task OnConnectedAsync()
{
try
{
Clients.Client(Context.ConnectionId).SendAsync("ReceiveConnID", Context.User.Identity.Name);
}
catch (Exception ex)
{
Clients.Caller.SendAsync("onError", "OnConnected:" + ex.Message);
}
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
try
{
}
catch (Exception ex)
{
Clients.Caller.SendAsync("onError", "OnDisconnected: " + ex.Message);
}
return base.OnDisconnectedAsync(exception);
}
}