How to arrange properly my ChatHub Messages in SignalR? - javascript

I used this code so far but i don't receive properly ordered messages output so can any one help me for proper output.
ChatHub Code :
public class ChatHub : Hub
{
static List<UserDetails> ConnectedUsers = new List<UserDetails>();
static List<MessageDetails> CurrentMessage = new List<MessageDetails>();
static List<TestProject1.Models.MessageDetails> CurrentMessage1 = new List<TestProject1.Models.MessageDetails>();
private TestContext db = new TestContext();
public void Connect(string userName)
{
var id = Context.ConnectionId;
var data = db.MessageDetails.ToList();
foreach (var item in data)
{
ConnectedUsers.Add(new UserDetails { ConnectionId = id, UserName = item.UserName });
CurrentMessage1.Add(new TestProject1.Models.MessageDetails { UserName = item.UserName, Message = item.Message });
}
Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage1);
Clients.AllExcept(id).onNewUserConnected(id, userName);
}
public void Send(string name, string message, string connection)
{
AddMessageinCache(name, message);
db.MessageDetails.Add(new TestProject1.Models.MessageDetails { UserName = name, Message = message });
db.SaveChanges();
Clients.All.addNewMessageToPage(name, message, connection);
}
private void AddMessageinCache(string userName, string message)
{
CurrentMessage.Add(new MessageDetails { UserName = userName, Message = message });
if (CurrentMessage.Count > 100)
CurrentMessage.RemoveAt(0);
}
}
I also include details of my html code here And Code of CSHTML:
<div id="page-content">
<div id='wrap'>
<div id="page-heading">
<ol class="breadcrumb">
<li>Dashboard</li>
<li>Chat Room</li>
</ol>
<h1>Chat Room</h1>
</div>
<div class="container">
<div class="col-md-12">
<div class="panel panel-inverse">
<div class="panel-heading">
<h4>Chat Room</h4>
<div class="options">
<i class="fa fa-cog"></i>
<i class="fa fa-refresh"></i>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-8">
<div class="panel-chat well" id="chat">
</div>
<form class="form-inline" action="#">
<div class="input-group">
<input type="text" placeholder="Enter your message here" id="message" class="form-control">
<span class="input-group-btn">
<button type="button" id="sendmessage" class="btn btn-primary"><i class="fa fa-comments-o"></i></button>
</span>
</div>
</form>
<input type="hidden" id="displayname" />
<input type="hidden" id="connection" />
</div>
</div>
</div>
</div>
</div>
</div>
</div> <!--wrap -->
</div> <!-- page-content -->
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
<script>
jQuery(function () {
$.connection.hub.url = 'http://localhost:34063//signalr';
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
chat.client.onConnected = function (id, userName, allUsers, messages) {
for (i = 0; i < messages.length; i++) {
AddMessage(messages[i].UserName, messages[i].Message);
}
}
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message, connectionid) {
// Add the message to the page.
$('#chat').append('<div class="chat-message me"><div class="chat-contact"><img src="/Content/assets/demo/avatar/avatar.png" alt=""></div><div class="chat-text">' + htmlEncode(name)
+ ': ' + htmlEncode(message) + '</div></div>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val('#Session["User"].ToString()');
$('#connection').val(#Session["UserID"]);
var name = $('#displayname').val();
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
var i = 0;
$.connection.hub.start().done(function () {
if (name.length > 0) {
if (i == 1) {
chat.server.connect(name);
}
}
$('#sendmessage').click(function () {
chat.server.send($('#displayname').val(), $('#message').val(), $('#connection').val());
$('#message').val('').focus();
});
});
i = i + 1;
$.connection.hub.disconnected(function () {
alert('Disconnected');
});
});
// This optional function html-encodes messages for display in the page.
function AddMessage(userName, message) {
$('#chat').append('<div class="chat-message me"><div class="chat-contact"><img src="/Content/assets/demo/avatar/avatar.png" alt=""></div><div class="chat-text">' + userName + ': ' + message + '</div></div>');
}
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
output is showing all messages but not showing in a sorted order and repeating messages on loading of a page. I want to remove it too.

Why you don't add DateTime stamps to messages. In your MessageDetails class add a property:
public DateTime ReceivedOn{get;set;}
Modify code that runs on receiving message:
private void AddMessageinCache(string userName, string message)
{
CurrentMessage.Add(new MessageDetails { UserName = userName, Message = message, ReceivedOn=DateTime.Now });
if (CurrentMessage.Count > 100)
CurrentMessage.RemoveAt(0);
}
On client side, you will need to sort messages on ReceivedOn property:
chat.client.onConnected = function (id, userName, allUsers, messages) {
$.sort(messages,function(a,b){
return a.ReceivedOn > b.ReceivedOn;
});
for (i = 0; i < messages.length; i++) {
AddMessage(messages[i].UserName, messages[i].Message);
}
}
Consult this question on sorting in jQuery.
EDIT:- On a side note, if only already received messages are a problem, then why don't fetch those sorted from database itself?

Related

How can I send the message to a specific user using the user's email address stored in db?

I have a project in ASP.NET Core 2.1 MVC SignalR. When I started the project I did not select Individual Account options in authorization. I want to send the message using the user's email address stored in a database instead of the connection Id but I can't achieve this. How can I fix this problem?
This is my hub class code:
public class SignalRChat:Hub
{
Context c = new Context();
public async Task setUserEmail(string email)
{
string id = Context.ConnectionId;
c.EmailConnectionIds.Where(a => a.connection_id == id).FirstOrDefault().email = email;
}
public async Task ClientSendMessage(string connectionId,string user, string message)
{
var deger= c.EmailConnectionIds.Where(a => a.connection_id ==
connectionId).FirstOrDefault();
await Clients.Client(deger.connection_id).SendAsync("ReceiveMessage",user, message);
}
public override async Task OnConnectedAsync()
{
EmailConnectionId val = new EmailConnectionId();
val.connection_id = Context.ConnectionId; ;
val.email = "";
c.EmailConnectionIds.Add(val);
c.SaveChanges();
await base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var connection = c.EmailConnectionIds.Where(a => a.connection_id ==
Context.ConnectionId).FirstOrDefault();
if (connection != null)
{
c.EmailConnectionIds.Remove(connection);
}
return base.OnDisconnectedAsync(exception);
}
}
This is my code:
"use strict";
$(document).ready(() => {
var connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();
var email = $("#client").val();
connection.start().then(() => connection.invoke('setUserEmail ', email));
$("#msg-send").click(() => {
let message = $("#txtMessage").val();
$("#txtMessage").val(" ");
var user = $("#sender").val();
connection.invoke("ClientSendMessage", $("#client").val(), user, message)
.catch(error => console.log("Error." + error));
var div = document.createElement("div");
div.textContent = message;
document.getElementById("chat-cont").appendChild(div);
});
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = msg;
var div = document.createElement("div");
div.textContent = encodedMsg;
document.getElementById("chat-cont").appendChild(div);
});
});
HTML code:
<div class="container">
<div id="chat-cont" class="clearfix" style="max-height:450px;overflow:scroll;">
</div>
<div style="margin-top:470px;margin-left:40%;bottom:50px !important;" class="footer navbar-fixed-bottom">
<div class="row">
<h5>Connection ID : <span id="connectionId"></span></h5>
</div>
<div class="row">
<div class="col-md-7"><input type="text" id="sender" value="#ViewBag.message"></div>
</div>
<div class="row">
<div class="col-md-7"><input type="text" placeholder="ReceiverId" id="client"></div>
</div>
<div class="row">
<div class="col-md-7" style="position:relative;"> <input type="text" id="txtMessage" class="form-control" style="width:70%;"></div>
<div class="col-md-5" style="position:absolute;margin-left:40%;"> <button id="msg-send" class="btn btn-success">Send</button></div>
</div>
</div>
</div>
I would suggest that you create a list of current connections CurrentConnections
static HashSet<EmailConnectionID> CurrentConnections = new HashSet<EmailConnectionID>();
class EmailConnectionID
{
public string email { get; set; }
public string connection_id { get; set; }
}
add the entries to CurrentConnections OnConnectedAsync
public override async Task<Task> OnConnectedAsync()
{
EmailConnectionID val = new EmailConnectionID();
val.connection_id = Context.ConnectionId; ;
val.email = "";
CurrentConnections.Add(val);
return base.OnConnectedAsync();
}
then after the client connect first thing you do is to send the email to the hub and edit the entry on the CurrentConnections list with the email
public async Task setUserEmail(string email)
{
string id = Context.ConnectionId;
CurrentConnections.Where(a => a.connection_id == id).FirstOrDefault().email = email;
}
you shold remove enties from CurrentConnections list OnDisconnectedAsync
public override Task OnDisconnectedAsync(Exception exception)
{
var connection = CurrentConnections.Where(a => a.connection_id ==
Context.ConnectionId).FirstOrDefault();
if (connection != null)
{
CurrentConnections.Remove(connection);
}
return base.OnDisconnectedAsync(exception);
}
after that you can send based on email address

jQuery click event isn't firing

The relevant Javascript follows below, but in short, the archive button works while the delete button does not. I've already tried moving the event handler to a different file which the HTML calls using script tags to see if that makes a difference, but it doesn't seem to, although I'm unsure if it's broken in the same way. Additionally, the actual functions associated with each event handler are practically the same, so it seems reasonable to rule out that the function itself causes the problem. Why are the two buttons performing differently?
const mongo = require('mongodb');
const config = require('../../javascripts/config.js'); //databaseAddress can now be found at config.databaseAddress()const mongo = require('mongodb');
const MongoClient = mongo.MongoClient;
const url = config.databaseAddress();
$(document).ready(function () {
$('#navbar-userSearch').addClass('active');
$('.archive-button').click(function () {
var id = $(this).attr('id').split('-').slice(-1)[0] ;
console.log('id', id);
var username = $('#username-' + id).val();
var csrf = $('#csrf').val();
var action = $(this).attr('id').split('-')[0];
console.log('username', username);
$.ajax({
url: '/'+action+'/'+username,
type: 'PUT',
data: {username: username, _csrf: csrf},
success: function(data) {
if (data.success) {
addMessage(data.message, true);
if (typeof data.redirect === 'string') {
setTimeout(function(){
window.location = data.redirect;
}, 2500);
}
} else {
addMessage(data.message, false);
}
},
error: function(err) {
addMessage('A network error might have occurred. Please try again.', false);
}
});
});
$('.delete-button').click(function() {
console.log("stop pushing my buttons");
var id = $(this).attr('id').split('-').slice(-1)[0] ;
console.log('id', id);
var username = $('#username-' + id).val();
console.log('username', username);
MongoClient.connect(url, { useNewUrlParser: true }, (err, client) => {
const db = client.db("paxpopulidb");
const id_query = {_id: id};
const username_query = db.collection("users").find(id_query, {_id: 0, username: 1});
const username = username_query.username;
if (username) {
if (username === "superadmin"){
console.log("You cannot delete the superadmin account.");
} else {
db.collection("registrations").deleteOne(username_query);
db.collection("users").deleteOne(username_query);
db.collection("schedules").deleteOne(username_query);
console.log("Deleted " + username + " from database.");
}}})
})});
The HTML uses Handlebars for templating and is as follows:
{{#each users}}
<div class='col-xs-12 col-ms-6 col-sm-4 col-md-3 col-lg-4 user-container tile-container' id='user-container-{{_id}}'>
<div class='tile user-tile' name="user-{{_id}}" data-id='{{_id}}'>
<div class='tile-icon' style='float: left'><img class='icon' src="/images/user.gif"></img></div>
<div class='user-header tile-header'>
<a data-toggle="modal" data-target='#user-modal-{{_id}}'>
<h4 class='tile-title'>{{#if fullName}}{{fullName}}{{else}}{{firstName}} {{lastName}}{{/if}}</h4>
</a>
<p class='tile-subtitle'>{{role}}<h class='small-text'>{{#if archived}}(archived){{/if}}</h></p>
</div>
</div>
<div id="user-modal-{{_id}}" class="user-item-modal modal fade" role="dialog">
<div class="modal-messages"></div>
<div class="modal-dialog modal-xs">
<div class="modal-content">
<div class="modal-header modal-title">
<button type="button" class="close" data-dismiss="modal">×</button>
{{#if fullName}}{{fullName}}{{else}}{{firstName}} {{lastName}}{{/if}}'s Profile
</div>
<div class="modal-body">
{{> profileTable}}
</div>
</div>
</div>
</div>
<div>
<input id='username-{{_id}}' type="hidden" value="{{username}}"></input>
<input id='requestToken-{{_id}}' type="hidden" value="{{requestToken}}"></input>
<input id='csrf' type="hidden" name="_csrf" value="{{csrfToken}}">
<center>
{{#isRegularUser role}}
<button id='registration-button-{{_id}}' class='btn btn-info btn-hg registration-button'>View/Edit Registration</button>
<button id='schedule-button-{{_id}}' class='btn btn-info btn-hg schedule-button'>View/Edit Schedule</button>
{{/isRegularUser}}
{{#ifNot archived}}
<button id='archive-button-{{_id}}' class='btn btn-warning btn-hg archive-button'>Deactivate</button>
{{else}}
{{/ifNot}}
{{#isRegularUser role}}
<button id='delete-button-{{_id}}' class='btn btn-danger btn-hg delete-button'>Delete User</button>
{{/isRegularUser}}
</center>
</div>
</div>
{{/each}}
In short, the above makes a small box with appropriate buttons for each user depending on their role attribute, but the only working button so far is archive-button (no event handlers exist for the other two yet) However, the delete-button actually displays, it's just that clicking it does nothing.
Your bracketing is wrong. You have the delete button event handler inside the archive button click handler. So the delete handler isn't added until you click on an archive button (and if you click on archive multiple times, the delete buttons will execute their code multiple times).
You would see this if you'd indented your code correctly (every programming editor has options to automate this for you).
It should be:
$(document).ready(function() {
$('#navbar-userSearch').addClass('active');
$('.archive-button').click(function() {
var id = $(this).attr('id').split('-').slice(-1)[0];
console.log('id', id);
var username = $('#username-' + id).val();
var csrf = $('#csrf').val();
var action = $(this).attr('id').split('-')[0];
console.log('username', username);
});
$('.delete-button').click(function() {
console.log("stop pushing my buttons");
var id = $(this).attr('id').split('-').slice(-1)[0];
console.log('id', id);
var username = $('#username-' + id).val();
console.log('username', username);
});
});

AngularJS Why is my Controller not responsive?

This is a difficult question to ask, I will do my best to be brief:
I have a simple controller that I want to use to get information from an API and populate a selection list from trello.
Here is my controller:
function TrelloController($scope, $location, $routeParams, $timeout, dialogs, common){
var vm = this;
var controllerId = 'TrelloController';
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var logError = getLogFn(controllerId, 'error');
var scope = $scope;
var TRELLO = require("trello");
var key = '<my key>';
var token = '<my token>';
var trello = new TRELLO(key, token);
vm.title = "Trello Controller";
vm.addCard = addCard;
vm.getBoards = getBoards;
vm.toggle = toggle;
vm.getLists = getLists;
vm.getListsFromDictionary = getListsFromDictionary;
vm.isTrelloActive = false;
activate();
function activate(){
common.activateController([], controllerId)
.then(function () {
log('Activated Trello Controller');
initialise();
});
}
function initialise() {
vm.isTrelloActive = false;
getBoards();
getLists();
}
function toggle() {
vm.isTrelloActive = !vm.isTrelloActive;
log("TOGGLE CLICKED");
}
function addCard(cardName, cardDescription, listId) {
trello.addCard(cardName, cardDescription, listId, function (error, cardAdded) {
if (error) {
log("Could Not Add Card: ", error);
} else {
log("Card added: ", cardAdded.name);
}
});
}
function getBoards() {
trello.getBoards("me", function (error, boards) {
if (error) {
log("Could Not Get Boards: ", error);
} else {
log("found " + boards.length + " boards");
console.log(boards);
}
scope.boards = boards;
});
}
function getLists(){
for (var i=0; i<scope.boards.length; i++){
getListsWithBoardId(scope.boards[i].id, i);
}
}
function getListsWithBoardId(boardId, index){
trello.getListsOnBoard(boardId, function(error, lists){
if (error) {
log("Could Not Get Boards: ", error);
} else {
log("found " + lists.length + " lists on board:"+boardId);
console.log(lists);
}
scope.boards[index].lists = lists;
});
}
function getListsFromDictionary(boardId){
for (var i=0; i<scope.boards.length; i++) {
if(scope.boards[i].id == boardId){
return scope.boards[i].lists;
}
}
}
}module.exports = TrelloController;
This controller is intended to serve the purpose of governing my dialogue, simplified, this is that dialogue:
<div data-ng-controller="TrelloController as vm">
<div class="modal-header">
<img class="trelloLogo" name="trelloLogo" src="public/content/images/trello-mark-blue.png" alt="Add To Trello" ng-click="vm.toggle}">
<h3>{{parameter.heading}}</h3>
</div>
<div class="modal-body">
<form name="form">
<div ng-if="vm.isTrelloActive" class="form-group">
<label>Board</label>
<select name="typeInput" class="form-control" ng-required="true" ng-model="form.boardInput">
<option selected>Choose Board</option>
<option ng-repeat="board in scope.boards" value="{{board.id}}">{{board.name}}</option>
</select>
</div>
</form>
</div>
<!-- This section contains parts in the vm.addCard(...) that aren't included in this shortened version of The HTML template, I provided it with the additional fields for context of the API call at the end -->
<div ng-if="vm.isTrelloActive" class="modal-footer">
<button class="btn btn-primary" ng-disabled="!form.$dirty || !form.$valid" ng-click="vm.addCard(form.titleInput, form.descriptionInput, form.listInput)">Add To Board</button>
<button class="btn btn-default" ng-click="vm.isTrelloActive=false">Cancel</button>
</div>
</div>
When I am in the dialogue, Pressing the logo button appears to do nothing even though when it was previously set to: ng-click="vm.isTrelloActive = !vm.isTrelloActive" it would toggle the entire page. The activate method produces no logs and does not appear to run when pressed.
Why is this happening?

SignalR dotnet core MVC Unit testing

and sorry for the confusing title!
so, i am developing a "lobby" where users come in, and other users can see when somebody logs in to the lobby. there can be many lobbies, but only when a user log in to the same lobby, they will show up. the solution and deployment works fine, my problem is (as the title suggests) testing this whole mess!
For this, i figured i need to test 2 things:
1. i need to test that signalR HUB side calls correctly when clients connects etc. so testing the server side part of SignalR
2. i need to test that my javascript, thats under the folder wwwroot/js (asp.net core mvc) for this i figure i need to use mocha. my problem here is that the js files is under a c# .net core project, and i dont know how to test it then.
However, after tirelessly searching forums and docs everywhere on the net, i came up with no solutions for none of the problems! i hope somebody can help, maybe hint some ideas of how i go about doing this.
i will post my relevant code here:
Lobby.cshtml
#using Domain.Interfaces
#using Domain.Models
#using GUI_Index.Session
#using Microsoft.AspNetCore.Http
#model GUI_Index.ViewModels.LobbyViewModel
#{
ViewData["Title"] = "Tilslut Lobby";
}
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<body>
<div class="container">
<button id="ForLadLobby" type="button" class="btn btn-post" onclick="location.href = '#Url.Action("ForladLobby", "Lobby", Model)'">Ud af lobby</button>
<div class="form-group left-Align ingamebox">
<table id="UsersInLobby">
<th>Users in Lobby: </th>
#{
foreach (var usernames in Model.Usernames)
{
<tr id="#usernames">
<td>
#usernames
</td>
</tr>
}
}
</table>
</div>
<div class="form-group right-Align ingamebox">
Message...<input type="text" id="messageInput" />
<input type="button" class="btn btn-primary" id="sendButton" value="Send Message" />
</div>
#{
string user = SessionExtension.GetObjectFromJson<User>(Context.Request.HttpContext.Session, "user").Username;
}
<form method="post" formaction="#Url.Action("Lobby", "Lobby")">
<table>
<tr>
<div class="top-Align">
<div id="center-Text">
Hello: <label id="LobbyUser">#user</label> <br/>
Welcome to: <label id="LobbyId">#Model.Id</label>
</div>
<ul id="Messages"></ul>
</div>
</tr>
<tr>
<div class="absolute-center is-responsive">
<div class="form-group">
<input type="hidden" class="form-control" name="Id">
</div>
<div class="btn-group-vertical">
#{
if (Model.Admin == user)
{
<button type="submit" class="btn btn-primary">Start spil</button>
<div class="divider"></div>
<br />
}
}
</div>
</div>
</tr>
</table>
</form>
</div>
</body>
<script src='#Url.Content("~/lib/signalr.js")'></script>
<script src='#Url.Content("~/js/Lobby.js")'></script>
Lobbyhub.cs
using System;
using System.Threading.Tasks;
using Domain.Models;
using GUI_Index.Session;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
// https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api
namespace GUI_Index.Hubs
{
public class LobbyHub : Hub
{
//private HttpContext context = new DefaultHttpContext();
/// <summary>
/// Called by SignalR on connection to page
/// </summary>
/// <returns></returns>
public override async Task OnConnectedAsync()
{
await this.Clients.Caller.SendAsync("Connect");
}
/// <summary>
/// called by Lobby.js
/// </summary>
/// <param name="username">The username of the user in the lobby</param>
/// <param name="Lobbyname">The lobbyname of the lobby where user is</param>
/// <returns></returns>
public async Task OnConnectedUserAsync(string username, string Lobbyname)
{
//add user to group
await Groups.AddAsync(Context.ConnectionId, Lobbyname);
//send to others in the group
await this.Clients.OthersInGroup(Lobbyname).SendAsync("onConnectedUser", username);
//old
//await this.Clients.Others.SendAsync("OnConnectedUser", username);
}
/// <summary>
/// Called by Lobby.js
/// </summary>
/// <param name="user"> the user in the lobby that sends</param>
/// <param name="LobbyName"> the lobby that the user is in</param>
/// <param name="message"> the message the user wishes to send</param>
/// <returns></returns>
public async Task SendMessageAsync(string user,string LobbyName, string message)
{
await this.Clients.Group(LobbyName).SendAsync("ReceiveMessage", user, message);
//old
//await this.Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task UserLeftAsync(string username, string lobbyname)
{
await this.Groups.RemoveAsync(Context.ConnectionId, lobbyname);
await this.Clients.OthersInGroup(lobbyname).SendAsync("OnDisconnectedUser", username);
}
/*
public override async Task OnDisconnectedAsync(Exception exception)
{
await this.Clients.All.SendAsync("Disconnect");
}
public async Task OnDisconnectedUserAsync(string username)
{
await this.Clients.Others.SendAsync("OnDisconnectedUser", username);
}
*/
}
}
Lobby.js
const connection = new signalR.HubConnection("/Hubs/LobbyHub", { logger: signalR.LogLevel.Information });
/////////////////////////////////////Enter Lobby///////////////////////////////////////////
connection.on("Connect", () => {
//get the username
var Username = document.getElementById("LobbyUser").textContent;
//get the lobbyName
var Lobbyname = document.getElementById("LobbyId").textContent;
//send to hub
connection.invoke("OnConnectedUserAsync", Username, Lobbyname);
//}
});
connection.on("OnConnectedUser",
(user) => {
if (!document.getElementById(user)) {
var li = document.createElement("li");
li.textContent = "User: " + user + " Signed On!";
document.getElementById("Messages").appendChild(li);
//update table
const table = document.getElementById("UsersInLobby");
const newrow = table.insertRow(table.rows.length);
//set the id of the row
newrow.id = user;
const newcell = newrow.insertCell(0);
//add user to table
const newText = document.createTextNode(user);
newcell.appendChild(newText);
}
});
///////////////////////////////////////Messages///////////////////////////////////////////////
document.getElementById("sendButton").addEventListener("click", event => {
//get the username
const user = document.getElementById("LobbyUser").textContent;
//get the message
const message = document.getElementById("messageInput").value;
//get the lobbyname
const lobby = document.getElementById("LobbyId").textContent;
//send it to hub
connection.invoke("SendMessageAsync", user,lobby, message).catch(err => console.error);
event.preventDefault();
});
connection.on("ReceiveMessage", (user, message) => {
//write the complete message
const Message = user + " says " + message;
//create list element
const li = document.createElement("li");
//add to list element
li.textContent = Message;
//append to chat
document.getElementById("Messages").appendChild(li);
});
///////////////////////////leave Lobby////////////////////////////////////
//Setup click event
document.getElementById("ForLadLobby").addEventListener("click", event => {
//get the username
const user = document.getElementById("LobbyUser").textContent;
//get the lobbyname
const lobby = document.getElementById("LobbyId").textContent;
//send it to hub
connection.invoke("UserLeftAsync", user, lobby).catch(err => console.error);
event.preventDefault();
});
//user left
connection.on("OnDisconnectedUser",
(user) => {
//create element to hold information
const li = document.createElement("li");
//tell others that user left
li.textContent = "User: " + user + " Signed Off!";
//add to chat
document.getElementById("Messages").appendChild(li);
//update table of online users
var row = document.getElementById(user);
//get the table and delete the row!
row.parentNode.removeChild(row);
//old
//row.deleteCell(0);
});
connection.start().catch(err => console.error);
I hope you can help me!

How do I send a message to an offline user in a SignalR chat program?

I'm using SignalR for the communication in my website and I need to be able to send messages to online users as well as those that are currently offline. I successfully embedded SignalR in my project and everything works fine except the offline chatting. I have no idea why this won't work.
Since I use Umbraco CMS in background, I can get access to the various IDs of the Umbraco member in simple manner. Therefore he has always the required IDs to build a group, both the sender ID and the receiver ID. So, why does the offline chat still not work? Why does the chat window not open when a user is offline?
I have attached my View and my Hub and I hope that somebody can help me.
My ChatHub
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.AspNet.SignalR.Transports;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
using System.Timers;
using Umbraco714.Modal;
using Umbraco.Core;
using Umbraco714.Repositories;
using Umbraco714.Models;
using System.Diagnostics;
using System.Collections;
namespace Umbraco714.Hubs
{
[HubName("chatHub")]
public class ChatHub : Hub
{
public Task JoinGroup()
{
return Groups.Add(Context.ConnectionId, "foo");
}
/*
Method to send a message or a message history to both chat partners in a specific group
*/
public void Send(string message, string groupName)
{
var chatrepo = new ChatRepository();
if (Clients != null)
{
// Call the addMessage method on all clients
Clients.Group(groupName).addMessage(message, groupName, chatrepo.GetAll(groupName));
if (chatrepo.GetAll(groupName).Count < 3)
{
var repo = new ChatRepository();
repo.Insert(new ChatMessage(message, groupName, DateTime.Now));
}
}
}
/*
Method which notify all existing online users about new user coming online by updating the online users list
*/
public override Task OnConnected()
{
var newUsers = OnlineUser.userObj.Where(item => item.newStatus == true).Select(item => item.userId).ToList();
UserModal user = OnlineUser.userObj.Where(item => item.sessionId == HttpContext.Current.Request.Cookies["ASP.NET_SessionId"].Value.ToString()).SingleOrDefault();
user.connectionId = Context.ConnectionId;
return Clients.All.joined(Context.ConnectionId, newUsers);
}
/*
Method to pass the list of the online users to the newly connected user
*/
public void GetAllOnlineStatus()
{
Clients.Caller.OnlineStatus(Context.ConnectionId, OnlineUser.userObj.Select(item => item.userId).ToList());
}
/*
Method to create a group of any given pair of users
*/
public string CreateGroup(string currentUserId, string toConnectTo)
{
string strGroupName = GetUniqueGroupName(currentUserId, toConnectTo);
string connectionId_To = OnlineUser.userObj.Where(item => item.userId == toConnectTo).Select(item => item.connectionId).SingleOrDefault();
if (!string.IsNullOrEmpty(connectionId_To))
{
var chatrepo = new ChatRepository();
//create the new group with the two member id's
Groups.Add(Context.ConnectionId, strGroupName);
Groups.Add(connectionId_To, strGroupName);
Clients.Caller.setChatWindow(strGroupName, toConnectTo, chatrepo.GetAll(strGroupName));
return strGroupName;
}
return null;
}
/*
Method to create a unique group name
*/
private string GetUniqueGroupName(string currentUserId, string toConnectTo)
{
return (currentUserId.GetHashCode() ^ toConnectTo.GetHashCode()).ToString();
}
}
}
My Chat View
#using System;
#using System.Collections.Generic;
#using System.Linq;
#using System.Web;
#using Umbraco714.Models;
#using Umbraco714.Repositories;
#using Umbraco.Core;
#using Umbraco.Core.Persistence;
#using Umbraco.Core.Services;
#using Umbraco.Web.Security;
#using Umbraco.Web;
#using Umbraco.Web.Models;
#using Umbraco.Core.Models;
#using System.Web.Security;
#{
var memberShipHelper = new Umbraco.Web.Security.MembershipHelper(Umbraco.Web.UmbracoContext.Current);
var memberId = memberShipHelper.GetCurrentMemberId();
var member = ApplicationContext.Current.Services.MemberService.GetById(memberId);
}
<div class="container">
<form id="chatForm">
<div id="divChatWindow" style="border: 1px solid #3366CC; float: left; width: 365px; display: none; margin-right: 10px">
<div id="messageBox" style="border: 1px solid blue;">
<ul id="messages" style="width: 365px; height: 100px; font: normal 2 verdana; font-size:12px; overflow: auto; margin-bottom: 0px; word-wrap: break-word;"></ul>
</div>
<hr />
<input type="text" id="msg" class="ChatText" style="width: 280px; height:22px; border-color: #666699; border-style: solid; border-width: 1px; " />
<input type="button" id="btnChatSend" class="ChatSend" value="Send" style="background-color: #99ccff; font-size: smaller; border: 1px solid #0066FF; height:24px" />
</div>
<br />
<label id="lblUserName" style="font-weight:bold" />
<br />
<div id="userList" style="border: thin solid #C0C0C0; font-family: Tunga; width: 150px; font-weight: 500;">
#foreach (var user in ApplicationContext.Current.Services.MemberService.GetAllMembers().OrderBy(n => n.Name))
{
if (#user.Properties["nickname"].Value != null)
{
if (#user.Id != #member.Id)
{
<img src="~/img/offline.png" data-userid="#user.Id" class="UserImg" width="18" height="18" />
<a class="UserItem" data-userid="#user.Id" href="#">#user.Properties["nickname"].Value.ToString()</a>
<br />
}
}
}
</div>
<div style="float: left" id="chatContainer">
</div>
</form>
<input type="hidden" id="hdnUserId" value="#ViewData["hdnUserId"]" />
<input type="hidden" id="hdnUserName" value="#ViewData["hdnUserName"]" />
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title"></h4>
</div>
<div class="modal-body">
<
</div>
<div class="modal-footer">
<input type="hidden" id="ConnectTo" value="0" name="ConnectTo" />
<button class="btn btn-default" data-dismiss="modal"></button>
<button id="SendRequest" value="SendRequest" name="ButtonId" class="btn btn-primary"></button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(document).ready(function () {
// Proxy created on the fly
var chat = $.connection.chatHub;
//Get called from the GetAllOnlineStatus methode in the Chat Hub to pass the online user list to the newly connected user
chat.client.OnlineStatus = function (connectionId, userList) {
$("img[id^=stat]").attr('src', 'img/offline.png');
$(userList).each(function (index, obj) {
$("div#userList a[data-userid='" + obj + "']").addClass('online');
$("div#userList img[data-userid='" + obj + "']").attr('src', 'img/online.png');
});
};
//Get called from the OnConnected methode in the Chat Hub to update the online user List for existing users
chat.client.joined = function (connectionId, userList) {
$(userList).each(function (index, obj) {
$("div#userList a[data-userid='" + obj + "']").addClass('online');
$("div#userList img[data-userid='" + obj + "']").attr('src', 'img/online.png');
});
};
// make the chat window visible
chat.client.setChatWindow = function (strGroupName, strChatTo, listMessages) {
if ($('div[groupname=' + strGroupName + ']').length == 0) {
$('div[chatToId=' + strChatTo + ']').attr('groupname', strGroupName);
$('div[chatToId=' + strChatTo + ']').css('display', 'block')
//load history from SendClient
for (var i = 0, l = listMessages.length; i < l; i++) {
$('div[groupname=' + strGroupName + ']').find('ul').append('<li>' + listMessages[i].message + '</li>').scrollTop($('html, body').height());
}
}
};
// Declare a function on the chat hub so the server can invoke it
chat.client.addMessage = function (message, groupName, listMessages) {
if ($('div[groupname=' + groupName + ']').length == 0) {
// Load history from the SendClient
for (var i = 0, l = listMessages.length; i < l; i++) {
$('#divChatWindow').find('#messages').append('<li>' + listMessages[i].message + '</li>');
}
var chatWindow = $("#divChatWindow").clone(true);
$(chatWindow).css('display', 'block');
$(chatWindow).attr('groupname', groupName);
$("#chatContainer").append(chatWindow);
}
if (listMessages.length < 3) {
$('div[groupname=' + groupName + ']').find('ul').append('<li>' + message + '</li>').scrollTop($('html, body').height());
} else {
var toId = $('div[groupname=' + groupName + ']').attr('chattoid')
console.log(toId)
$('#myModal').find('#ConnectTo').val(toId)
$('#myModal').modal()
}
};
$("#broadcast").click(function () {
// Call the chat method on the server
chat.server.send($('#msg').val());
});
// Start the connection
$.connection.hub.start(function () {
chat.server.getAllOnlineStatus();
});
$('.UserItem').click(function () {
// Pass the Id from the current logged in Member and his chat partner to the Hub method CreateGroup
chat.server.createGroup($('#hdnUserId').val(), $(this).attr('data-userid'));
var chatWindow = $("#divChatWindow").clone(true);
$(chatWindow).attr('chatToId', $(this).attr('data-userid'));
$("#chatContainer").append(chatWindow);
return false;
});
// submit button click event
$(".ChatSend").click(function () {
strChatText = $('.ChatText', $(this).parent()).val();
if (strChatText != '') {
var strGroupName = $(this).parent().attr('groupname');
if (typeof strGroupName !== 'undefined' && strGroupName !== false)
chat.server.send($("#hdnUserName").val() + ' : ' + strChatText, $(this).parent().attr('groupname'));
$('.ChatText', $(this).parent()).find('ul').append(strChatText);
$('.ChatText', $(this).parent()).val('');
}
return false;
});
$(".ChatText").keypress(function (e) {
if (e.keyCode == 13) {
strChatText = $('.ChatText', $(this).parent()).val();
if (strChatText != '') {
var strGroupName = $(this).parent().attr('groupname');
if (typeof strGroupName !== 'undefined' && strGroupName !== false)
chat.server.send($("#hdnUserName").val() + ' : ' + strChatText, $(this).parent().attr('groupname'));
$('.ChatText', $(this).parent()).find('ul').append(strChatText);
$('.ChatText', $(this).parent()).val('');
}
}
});
});
</script>
You need to track state of the user (that ability is not build into signalR), and only send data to users that are online.
Have a look at this on how to track online users , https://www.simple-talk.com/dotnet/asp.net/tracking-online-users-with-signalr/

Categories

Resources