Last active
August 29, 2015 14:02
-
-
Save vcardins/862f8c4cff124ac08873 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web.Http; | |
using Microsoft.AspNet.SignalR; | |
using Omu.ValueInjecter; | |
using SimpleInjector; | |
using TechsApp.Core.Extensions.Membership; | |
using TechsApp.Core.Interfaces.Data; | |
using TechsApp.Core.Interfaces.Service; | |
using TechsApp.Core.Models; | |
using TechsApp.Core.Models.Account; | |
using TechsApp.Core.ViewModels.Partnership; | |
using TechsApp.Core.ViewModels.Users; | |
using TechsApp.Infrastructure.Data; | |
using TechsApp.Web.Models; | |
using WebBackgrounder; | |
using IDependencyResolver = System.Web.Http.Dependencies.IDependencyResolver; | |
using Task = System.Threading.Tasks.Task; | |
namespace TechsApp.Web.Hubs | |
{ | |
[Microsoft.AspNet.SignalR.Authorize] | |
public abstract class BaseHub : Hub | |
{ | |
private readonly IDataContext _datacontext; | |
protected int UserId; | |
protected string Username; | |
public static string ConnectionId; | |
protected static readonly ConcurrentDictionary<string, AppUser> AppUsers = new ConcurrentDictionary<string, AppUser>(); | |
protected static readonly ConcurrentDictionary<string, AppUser> OfflineUsers = new ConcurrentDictionary<string, AppUser>(); | |
protected AppUser Me; | |
protected static readonly ConcurrentDictionary<string, IList<UserKeys>> Partners = new ConcurrentDictionary<string, IList<UserKeys>>(); | |
protected static readonly JobHost _jobHost = new JobHost(); | |
protected IDependencyResolver _di; | |
protected static IUserService _userService; | |
protected IQueryable<User> _queryUser; | |
protected IQueryable<Chat> _queryChat; | |
//protected static IPartnershipService _partnershipService; | |
//protected static IUserMobileDeviceService _mobileDeviceService; | |
protected BaseHub() | |
{ | |
_di = GlobalConfiguration.Configuration.DependencyResolver; | |
//_partnershipService = (IPartnershipService)_di.GetService(typeof(IPartnershipService)); | |
//_mobileDeviceService = (IUserMobileDeviceService)_di.GetService(typeof(IUserMobileDeviceService)); | |
_userService = (IUserService)_di.GetService(typeof(IUserService)); | |
//_datacontext = new DatabaseFactory().Get(); | |
} | |
public override Task OnConnected() | |
{ | |
Connect(true); | |
return base.OnConnected(); | |
} | |
public override Task OnReconnected() | |
{ | |
Connect(false); | |
return base.OnReconnected(); | |
} | |
private string GetDeviceId() | |
{ | |
var headers = Context.Request.Headers; | |
return (headers!=null) ? headers["X-ATT-DeviceId"] : null; | |
} | |
protected void Connect(bool isOnConnected) | |
{ | |
UserId = Context.User.GetUserID(); | |
Username = Context.User.Identity.Name; | |
ConnectionId = Context.ConnectionId; | |
var appUser = GetUserByConnectionId(ConnectionId); | |
if (appUser != null) | |
return; | |
appUser = AddUser(); | |
Me = appUser; | |
Groups.Add(ConnectionId, Username); | |
} | |
//public override Task OnDisconnected() | |
//{ | |
// Disconnect(); | |
// return base.OnDisconnected(); | |
//} | |
public IList<AppUser> OnlineUsers() | |
{ | |
var users = new List<AppUser>(); | |
if (AppUsers.Count == 0) | |
return users; | |
users = (List<AppUser>)AppUsers.Select(user => user.Value); | |
return users.ToList(); | |
} | |
public IList<BaseAppUser> OnlinePartners() | |
{ | |
var appUser = GetUserByUsername(Context.User.Identity.Name); | |
return (appUser!=null) ? appUser.Partners.Where(pp => GetUserByUsername(pp.Username) != null).ToList() : new List<BaseAppUser>(); | |
} | |
//POST | |
public void SetStatus(bool status) | |
{ | |
var userId = Context.User.GetUserID(); | |
var username = Context.User.Identity.Name; | |
var user = GetUserByUsername(username); | |
if (user == null) | |
return; | |
user.IsMobileActive = status; | |
var result = new | |
{ | |
U = username, | |
I = userId, | |
T = DateTimeOffset.UtcNow.ToLocalTime(), | |
P = user.Partners, | |
M = user.IsMobileAccess, | |
A = user.IsMobileActive | |
}; | |
foreach (var p in user.Partners) | |
{ | |
Clients.Group(p.Username).onMobileStatusChange(result); | |
} | |
} | |
protected object Disconnect() | |
{ | |
var username = Context.User.Identity.Name; | |
//var appUser = GetUserByUsername(username); | |
//var connId = appUser.ConnectionIds.FirstOrDefault(x => x == Context.ConnectionId); | |
//appUser.ConnectionIds.Remove(connId); | |
var partners = OnlinePartners(); | |
foreach (var p in partners) //.Where(p => !appUser.ConnectionIds.Any()) | |
{ | |
//Remove the user group for the just disconnected user | |
Clients.Group(p.Username).onUserDisconnected(username); | |
} | |
//remove the leaving users from all partners objects | |
var ok = RemoveUser(); | |
return ok; | |
} | |
protected AppUser GetUserByUserId(int userId) | |
{ | |
return AppUsers.Values.FirstOrDefault(u => u.UserId == userId); | |
} | |
protected AppUser GetUserByUsername(string username) | |
{ | |
AppUser appUser; | |
AppUsers.TryGetValue(username, out appUser); | |
return appUser; | |
} | |
protected bool RemoveUser() | |
{ | |
AppUser appUser; | |
var username = Context.User.Identity.Name; | |
return AppUsers.TryRemove(username, out appUser); | |
} | |
protected UserHubInfo GetHubInfo(int userId) | |
{ | |
UserId = Context.User.GetUserID(); | |
_queryUser = _datacontext.DbSet<User>(); | |
var user = _queryUser.FirstOrDefault(u => u.UserId == userId); | |
if (user == null) | |
return null; | |
var userHubInfo = new UserHubInfo().InjectFrom(user) as UserHubInfo; | |
if (user.MobileDevice != null && userHubInfo != null) | |
{ | |
userHubInfo.DeviceId = user.MobileDevice.Token; | |
userHubInfo.DeviceOS = user.MobileDevice.Model; | |
} | |
if (user.Partnerships == null) return userHubInfo; | |
foreach (var p in user.Partnerships) | |
{ | |
userHubInfo.Partners.Add( | |
new UserKeys | |
{ | |
DisplayName = p.Partner.DisplayName, | |
UserId = p.PartnerId, | |
Username = p.Partner.Username | |
}); | |
} | |
return userHubInfo; | |
} | |
protected AppUser AddUser() | |
{ | |
AppUser appUser; | |
UserId = Context.User.GetUserID(); | |
Username = Context.User.Identity.Name; | |
ConnectionId = Context.ConnectionId; | |
var deviceId = GetDeviceId(); | |
if (AppUsers.TryGetValue(Username, out appUser)) | |
{ | |
appUser.ConnectionIds.Add(ConnectionId); | |
} | |
else | |
{ | |
var user = _userService.GetHubInfo(UserId); | |
if (user==null) | |
{ | |
return null; | |
} | |
//if (!string.IsNullOrEmpty(user.DeviceId) && !deviceId.Equals(user.DeviceId)) | |
//{ | |
// Clients.Caller.notAuthorized("Device token invalid"); | |
// return null; | |
//} | |
appUser = new AppUser | |
{ | |
Username = Username, | |
UserId = Context.User.GetUserID(), | |
ConnectionIds = new List<string> {ConnectionId}, | |
DeviceId = deviceId, | |
DeviceOS = user.DeviceOS, | |
Partners = user.Partners.Select(p => new BaseAppUser().InjectFrom(p) as BaseAppUser).ToList() | |
}; | |
AppUsers.TryAdd(Username, appUser); | |
} | |
//At first, if the user connected from mobile, both properties are true | |
appUser.IsMobileAccess = !string.IsNullOrEmpty(deviceId); | |
appUser.IsMobileActive = !string.IsNullOrEmpty(deviceId); | |
appUser.Updated = DateTimeOffset.UtcNow; | |
return appUser; | |
} | |
protected AppUser GetUserByConnectionId(string connectionId) | |
{ | |
return AppUsers.Values.FirstOrDefault(u => u.ConnectionIds.Any(z => z == connectionId)); | |
} | |
internal Scope Scope { get; set; } | |
protected override void Dispose(bool disposing) | |
{ | |
if (disposing && Scope != null) | |
{ | |
Scope.Dispose(); | |
} | |
base.Dispose(disposing); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Web.Http; | |
using Omu.ValueInjecter; | |
using TechsApp.Core.Extensions.Membership; | |
using TechsApp.Core.Interfaces.Messaging; | |
using TechsApp.Core.Interfaces.Service; | |
using TechsApp.Core.Models.Messaging; | |
using TechsApp.Core.ViewModels.Chat; | |
using TechsApp.Web.Models; | |
namespace TechsApp.Web.Hubs | |
{ | |
public class ChatHub : BaseHub | |
{ | |
private static IChatService _chatService; | |
private readonly IPushNotificationService _pushService; | |
public ChatHub() | |
{ | |
_di = GlobalConfiguration.Configuration.DependencyResolver; | |
_chatService = (IChatService)_di.GetService(typeof(IChatService)); | |
_pushService = (IPushNotificationService)_di.GetService(typeof(IPushNotificationService)); | |
} | |
//GET | |
public IEnumerable<ChatMessageResponse> GetMessages(string partnerUsername) | |
{ | |
var messages = _chatService.GetLastest(Context.User.Identity.Name, partnerUsername, 10).ToList(); | |
return messages; | |
} | |
//POST | |
public void SendMessage(string to, string message, string guid) | |
{ | |
var fromUser = Context.User; | |
var fromUserId = fromUser.GetUserID(); | |
var from = fromUser.Identity.Name; | |
var gid = guid ?? Guid.NewGuid().ToString(); | |
var chatResponse = new ChatMessageResponse | |
{ | |
FromUserId = fromUserId, | |
From = from, | |
To = to, | |
Message = message, | |
Guid = gid, | |
Created = DateTime.Now | |
}; | |
//Try to get the user from the Online dictionary | |
var userTo = GetUserByUsername(to); | |
if (userTo == null) | |
{ | |
//If not online, check the latest offline ones who tried to send message | |
if (!OfflineUsers.TryGetValue(to, out userTo)) | |
{ | |
//If not yet, get the mobile record | |
var user = _userService.GetHubInfo(UserId); | |
// if the mobile is registered, add the user in the offline collection and send a push | |
if (user != null) | |
{ | |
OfflineUsers.TryAdd(to, userTo = new AppUser().InjectFrom(user) as AppUser); | |
} | |
} | |
userTo = userTo ?? new AppUser(); | |
} | |
if (!userTo.IsMobileActive && !string.IsNullOrEmpty(userTo.DeviceId)) | |
{ | |
userTo.UnreadMessages++; | |
var push = new PushNotification | |
{ | |
Data = new Dictionary<string, object> | |
{ | |
{"from", from }, | |
{"to",to }, | |
{"message", message}, | |
{"guid", gid}, | |
{"created", chatResponse.Created} | |
}, | |
Message = userTo.DisplayName + "\r\n" + message, | |
DeviceToken = userTo.DeviceId, | |
Channel = userTo.Username, | |
Badge = userTo.UnreadMessages, | |
Sound = "new-message", | |
Device = userTo.DeviceOS | |
}; | |
_pushService.Send(push); | |
} | |
Clients.Group(to).receiveMessage(chatResponse); | |
Clients.Group(from).receiveMessage(chatResponse); | |
var chatInput = new ChatMessageInput | |
{ | |
FromUserId = fromUserId, | |
From = from, | |
To = to, | |
Message = message | |
}; | |
Action persistChat = () => PersistMessage(chatInput); | |
_jobHost.DoWork(new Task(persistChat)); | |
} | |
//POST | |
public object HandleTyping(string to) | |
{ | |
var from = Context.User.Identity.Name; | |
var obj = new {From = from, To = to}; | |
Clients.Group(to).listenTyping(obj); | |
return obj; | |
} | |
private static void PersistMessage(ChatMessageInput model) | |
{ | |
_chatService.Save(model); | |
} | |
//POST | |
public void SetMessagesAsRead() | |
{ | |
var username = Context.User.Identity.Name; | |
var user = AppUsers[username]; | |
if (user != null) | |
{ | |
AppUsers[username].UnreadMessages = 0; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static void RegisterServices(Container container) | |
{ | |
var hybridLifestyle = Lifestyle.CreateHybrid( | |
() => container.GetCurrentExecutionContextScope() != null, | |
new ExecutionContextScopeLifestyle(), | |
new WebRequestLifestyle()); | |
// infrastructure | |
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>(); | |
container.RegisterPerWebRequest<IDatabaseFactory, DatabaseFactory>(); | |
... | |
container.Register<IChatService, ChatService>(hybridLifestyle); | |
container.Register<IChatRepository, ChatRepository>(hybridLifestyle); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
namespace TechsApp.Web.Hubs | |
{ | |
public class UserPresenceHub : BaseHub | |
{ | |
//private static IConnectionService _connService; | |
private readonly object _sync = new object(); | |
public override Task OnConnected() | |
{ | |
base.OnConnected(); | |
return HandleConnection(); | |
} | |
public override Task OnReconnected() | |
{ | |
base.OnReconnected(); | |
return HandleConnection(); | |
} | |
public override Task OnDisconnected() | |
{ | |
Disconnect(); | |
return base.OnDisconnected(); | |
} | |
private Task HandleConnection() | |
{ | |
var onLinePartners = OnlinePartners(); | |
//Tell all my partners that I'm online by calling the client method : onUserConnected | |
var appUser = GetUserByUsername(Context.User.Identity.Name); | |
Me = appUser; | |
foreach (var p in onLinePartners) | |
{ | |
Clients.Group(p.Username).onUserConnected(new { User = appUser, Timestamp = DateTime.Now }); | |
} | |
//Call the client userConnected method for all users contained in the in just connected user group | |
return Clients.Caller.onConnected(new { Me, Partners = onLinePartners }); | |
} | |
public object SignOff() | |
{ | |
return new { Data = Disconnect() }; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This does not work ...
if I have:
container.RegisterPerWebRequest<IChatService, ChatService>();
container.RegisterPerWebRequest<IChatRepository, ChatRepository>();
Sometimes it does, sometimes it does not work.