1
votes

I've implemented ng-chat https://github.com/rpaschoal/ng-chat (SignalR).

I have 3 users: User1, User2 and User3

If I send a message from User1 to User2 it works well User2 receives the message, but if I create a group (with User1 I open User2's chat and then Add the User3) a new group is created with Users (User2 and User3).

So, when I send a message from this new chat, the users (User2 and User3) doesn't receive any message

Here is my SingalR Hub:

using AdvansysOficina.Api._Core.Infraestructura;
using AdvansysOficina.Api.Generales.Servicios.UsuarioNs;
using Microsoft.AspNetCore.SignalR;
using NgChatSignalR.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AdvansysOficina.Api.Desarrollo.Servicios.ConversacionPuntoNs.HubNs
{
public class ConversacionHub : Hub
{
    private static List<ParticipantResponseViewModel> AllConnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
    private static List<ParticipantResponseViewModel> DisconnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
    private readonly object ParticipantsConnectionLock = new object();

    private ISesion _sesion;
    private IUsuarioServicio _usuarioServicio;

    public ConversacionHub(ISesion sesion, IUsuarioServicio usuarioServicio)
    {
        _sesion = sesion;
        _usuarioServicio = usuarioServicio;
    }

    public static IEnumerable<ParticipantResponseViewModel> ConnectedParticipants(string currentUserId)
    {
        return AllConnectedParticipants
            .Where(x => x.Participant.Id != currentUserId);
    }

    public void Join(string userName, dynamic grupo)
    {
        lock (ParticipantsConnectionLock)
        {
            AllConnectedParticipants.Add(new ParticipantResponseViewModel()
            {
                Metadata = new ParticipantMetadataViewModel()
                {
                    TotalUnreadMessages = 0
                },
                Participant = new ChatParticipantViewModel()
                {
                    DisplayName = userName,
                    Id = Context.ConnectionId,
                }
            });


            // This will be used as the user's unique ID to be used on ng-chat as the connected user.
            // You should most likely use another ID on your application
            //Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);

            Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);

            Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
        }
    }

    public void SendMessage(MessageViewModel message)
    {

        var sender = AllConnectedParticipants.Find(x => x.Participant.Id == message.FromId);

        if (sender != null)
        {
            Clients.Client(message.ToId).SendAsync("messageReceived", sender.Participant, message);
        }
    }

    public override Task OnDisconnectedAsync(Exception exception)
    {
        lock (ParticipantsConnectionLock)
        {
            var connectionIndex = AllConnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);

            if (connectionIndex >= 0)
            {
                var participant = AllConnectedParticipants.ElementAt(connectionIndex);

                AllConnectedParticipants.Remove(participant);
                DisconnectedParticipants.Add(participant);

                Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
            }

            return base.OnDisconnectedAsync(exception);
        }
    }

    public override Task OnConnectedAsync()
    {
        lock (ParticipantsConnectionLock)
        {
            var connectionIndex = DisconnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);

            if (connectionIndex >= 0)
            {
                var participant = DisconnectedParticipants.ElementAt(connectionIndex);

                DisconnectedParticipants.Remove(participant);
                AllConnectedParticipants.Add(participant);

                Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
            }

            return base.OnConnectedAsync();
        }
    }
}
}

My signalR Adapter (Angular)

import { ChatAdapter, Message, ParticipantResponse, Group, IChatController } from 'ng-chat';
import { map, catchError } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

import * as signalR from '@aspnet/signalr';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AlertasHelper } from '../../../shared/helpers/alertas.helper';
import { PushNotificationHelper } from './notifications/push-notification';

export class SignalRAdapter extends ChatAdapter {
  public static serverBaseUrl  =  'http://192.168.16.51:5021/'; // if running locally
  public userId: string;
  private grrupo;
  private hubConnection: signalR.HubConnection;


  constructor(private username: string, private http: HttpClient, private notification: PushNotificationHelper
    ) {
    super();

    this.initializeConnection();
  }

  private initializeConnection(): void {
    this.hubConnection = new signalR.HubConnectionBuilder()
      .withUrl(`${SignalRAdapter.serverBaseUrl}chat`, { transport: signalR.HttpTransportType.LongPolling })
      .build();

    this.hubConnection
      .start()
      .then(() => {
        this.joinRoom();

        this.initializeListeners();
      })
      .catch(err => console.log(`Error while starting SignalR connection: ${err}`));
  }

  private initializeListeners(): void {
    this.hubConnection.on('generatedUserId', (userId) => {
      // With the userId set the chat will be rendered
      this.userId = userId;
    });

    this.hubConnection.on('messageReceived', (participant, message) => {
      // Handle the received message to ng-chat
      console.log(message);
      this.notification.notify('Nuevo mensaje de: ' + participant.displayName, message);
      this.onMessageReceived(participant, message);
    });

    this.hubConnection.on('friendsListChanged', (participantsResponse: Array<ParticipantResponse>) => {
      // Handle the received response to ng-chat
      this.onFriendsListChanged(participantsResponse.filter(x => x.participant.id !== this.userId));
    });
  }

  joinRoom(): void {
    if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
      this.hubConnection.send('join', this.username, '');
    }
  }

  listFriends(): Observable<ParticipantResponse[]> {
    // List connected users to show in the friends list
    // Sending the userId from the request body as this is just a demo
    // return this.http
    //   .post(`${SignalRAdapter.serverBaseUrl}listFriends`, { currentUserId: this.userId })
    //   .pipe(
    //     map((res: any) => res),
    //     catchError((error: any) => Observable.throw(error.error || 'Server error'))
    //   );
    return of([]);

  }

  getMessageHistory(destinataryId: any): Observable<Message[]> {
    // This could be an API call to your web application that would go to the database
    // and retrieve a N amount of history messages between the users.
    return of([]);
  }

  sendMessage(message: Message): void {
    if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
      console.log(message);
      this.hubConnection.send('sendMessage', message);
    }
  }

  groupCreated(group: Group): void {
    console.log( group);
  }
}

Use of component

<ng-chat #chat *ngIf="signalRAdapter && signalRAdapter.userId"
  [adapter]="signalRAdapter"
  [userId]="signalRAdapter.userId"
  [groupAdapter]="signalRAdapter"
  (onParticipantChatOpened)="chatOpened($event)"
  [historyEnabled]="false">
</ng-chat>

I've downloaded the example of github's creator page, but he doesn't have an example with signalr using groups, I hope you can help me.

1

1 Answers

3
votes

ng-chat treats groups as individual participants. You will have to join your room when this event gets invoked:

groupCreated(group: Group): void { console.log( group); // Invoke your SignalR hub and send the details of the newly created group }

ng-chat will generate unique ids every time a group is created so you can track which group is which whenever one gets created from a running ng-chat instance. How you will handle the persistence of these groups is up to your application.

You might want to push a notification to involved users from your SignalR adapter that their friends list has changed (They'll be able to see the group at this stage). You could also decide not to do so and only push a notification if the user who has created the group send an initial message (Once again, up to your application requirements and needs).

You might also want to implement IChatGroupAdapter on your adapter to make the contract more explicit.

Hope this helps!