using FirebaseAdmin.Messaging; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Entitlements; using Marco.Pms.Services.Service.ServiceInterfaces; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; namespace Marco.Pms.Services.Service { public class FirebaseService : IFirebaseService { private readonly IDbContextFactory _dbContextFactory; private readonly IServiceScopeFactory _serviceScopeFactory; public FirebaseService(IDbContextFactory dbContextFactory, IServiceScopeFactory serviceScopeFactory) { _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); } public async Task SendLoginMessageAsync(string name) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); // List of device registration tokens to send the message to var registrationTokens = await _context.FCMTokenMappings.Select(ft => ft.FcmToken).ToListAsync(); var notificationFirebase = new Notification { Title = "Login Alert", Body = $"{name} has successfully logged in to the application" }; await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); } // Attendance Controller public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid employeeId, Guid tenantId) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var teamAttendanceRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var regularizeAttendanceRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.RegularizeAttendance) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, teamAttendanceRoleTask, manageProjectsRoleTask, regularizeAttendanceRoleTask); var teamAttendanceRoleIds = teamAttendanceRoleTask.Result; var regularizeAttendanceRoleIds = regularizeAttendanceRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && teamAttendanceRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); var dataNotificationIds = new List(); var mesaageNotificationIds = new List(); Notification notificationFirebase; switch (markType) { case ATTENDANCE_MARK_TYPE.CHECK_IN: notificationFirebase = new Notification { Title = "Attendance Update", Body = $" {name} has checked in for project {project?.ProjectName ?? ""}." }; mesaageNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.CHECK_OUT: notificationFirebase = new Notification { Title = "Attendance Update", Body = $" {name} has checked out for project {project?.ProjectName ?? ""}." }; mesaageNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE: notificationFirebase = new Notification { Title = "Regularization Request", Body = $" {name} has submitted a regularization request for project {project?.ProjectName ?? ""}." }; mesaageNotificationIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && regularizeAttendanceRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); dataNotificationIds = employeeIds; break; case ATTENDANCE_MARK_TYPE.REGULARIZE: notificationFirebase = new Notification { Title = " Regularization Approved", Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been accepted." }; mesaageNotificationIds.Add(employeeId); dataNotificationIds.AddRange(employeeIds); break; case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT: notificationFirebase = new Notification { Title = "Regularization Denied", Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been rejected." }; mesaageNotificationIds.Add(employeeId); dataNotificationIds.AddRange(employeeIds); break; default: notificationFirebase = new Notification { Title = "Attendance Update", Body = $" {name} has update his/her attendance for project {project?.ProjectName ?? ""}." }; break; } // List of device registration tokens to send the message to var data = new Dictionary() { { "Keyword", "Attendance" }, { "ProjectId", projectId.ToString() }, { "Action", markType.ToString() } }; var registrationTokensForNotificationTask = Task.Run(async () => { if (mesaageNotificationIds.Any()) { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => mesaageNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } }); var registrationTokensForDataTask = Task.Run(async () => { if (dataNotificationIds.Any()) { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => dataNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); } }); await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); } // Task Controller public async Task SendAssignTaskMessageAsync(Guid workItemId, string name, List teamMembers, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var workItem = await _context.WorkItems .Include(wi => wi.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(wi => wi.Id == workItemId && wi.WorkArea != null && wi.WorkArea.Floor != null && wi.WorkArea.Floor.Building != null); if (workItem == null) { return; } var projectId = workItem.WorkArea!.Floor!.Building!.ProjectId; var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var viewTaskRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, viewTaskRoleTask, manageProjectsRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; var buildingName = workItem.WorkArea.Floor.Building.Name; var FloorName = workItem.WorkArea.Floor.FloorName; var AreaName = workItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); var data = new Dictionary() { { "Keyword", "Assign_Task" }, { "ProjectId", projectId.ToString() } }; // List of device registration tokens to send the message to var registrationTokensForNotificationTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => teamMembers.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); var notificationFirebase = new Notification { Title = $"Task Assigned for {project?.ProjectName}", Body = $"A task has been assigned to you by {name} at {location}" }; await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); }); var registrationTokensForDataTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); }); await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendReportTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var taskAllocation = await _context.TaskAllocations .Include(t => t.WorkItem) .ThenInclude(wi => wi!.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(t => t.Id == taskAllocationId && t.TenantId == tenantId && t.WorkItem != null && t.WorkItem.WorkArea != null && t.WorkItem.WorkArea.Floor != null && t.WorkItem.WorkArea.Floor.Building != null); if (taskAllocation == null) { return; } var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var teamMemberTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var viewTaskRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.TaskMembers .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) .Select(tm => tm.EmployeeId).ToListAsync(); }); var approveTaskRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ApproveTask) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, teamMemberTask, viewTaskRoleTask, manageProjectsRoleTask, approveTaskRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var teamMembers = teamMemberTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var approveTaskRoleIds = approveTaskRoleTask.Result; var project = projectTask.Result; var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var dataNotificationTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var employeeIds = await dbContext.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); employeeIds.AddRange(teamMembers); return employeeIds; }); var mesaageNotificationTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && approveTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); }); await Task.WhenAll(dataNotificationTask, mesaageNotificationTask); var dataNotificationIds = dataNotificationTask.Result; var mesaageNotificationIds = mesaageNotificationTask.Result; var data = new Dictionary() { { "Keyword", "Report_Task" }, { "ProjectId", projectId.ToString() }, { "TaskAllocationId", taskAllocationId.ToString() } }; // List of device registration tokens to send the message to var registrationTokensForNotificationTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => mesaageNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); var notificationFirebase = new Notification { Title = $"New Task Reported - {project?.ProjectName}", Body = $"{name} reported a task at {location}." }; await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); }); var registrationTokensForDataTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => dataNotificationIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); }); await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendTaskCommentMessageAsync(Guid taskAllocationId, string name, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var taskAllocation = await _context.TaskAllocations .Include(t => t.WorkItem) .ThenInclude(wi => wi!.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(t => t.Id == taskAllocationId && t.TenantId == tenantId && t.WorkItem != null && t.WorkItem.WorkArea != null && t.WorkItem.WorkArea.Floor != null && t.WorkItem.WorkArea.Floor.Building != null); if (taskAllocation == null) { return; } var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var viewTaskRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.TaskMembers .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) .Select(tm => tm.EmployeeId).ToListAsync(); }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, viewTaskRoleTask, manageProjectsRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); employeeIds.Add(taskAllocation.AssignedBy); var data = new Dictionary() { { "Keyword", "Task_Comment" }, { "ProjectId", projectId.ToString() }, { "TaskAllocationId", taskAllocationId.ToString() } }; // List of device registration tokens to send the message to var registrationTokensForNotification = await _context.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); var notificationFirebase = new Notification { Title = $"New Comment on Task - {project?.ProjectName}", Body = $"{name} added a comment on Task at {location}." }; await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendApproveTaskMessageAsync(Guid taskAllocationId, string name, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var taskAllocation = await _context.TaskAllocations .Include(t => t.WorkItem) .ThenInclude(wi => wi!.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(t => t.Id == taskAllocationId && t.TenantId == tenantId && t.WorkItem != null && t.WorkItem.WorkArea != null && t.WorkItem.WorkArea.Floor != null && t.WorkItem.WorkArea.Floor.Building != null); if (taskAllocation == null) { return; } var projectId = taskAllocation.WorkItem!.WorkArea!.Floor!.Building!.ProjectId; var projectTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.ProjectAllocations .Include(pa => pa.Project) .Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null) .GroupBy(pa => pa.ProjectId) .Select(g => new { ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() }).FirstOrDefaultAsync(); }); var teamMemberTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); var viewTaskRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.TaskMembers .Where(tm => tm.TaskAllocationId == taskAllocationId && tm.TenantId == tenantId) .Select(tm => tm.EmployeeId).ToListAsync(); }); var manageProjectsRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageProject) .Select(rp => rp.ApplicationRoleId).ToListAsync(); }); await Task.WhenAll(projectTask, teamMemberTask, viewTaskRoleTask, manageProjectsRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var teamMembers = teamMemberTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var project = projectTask.Result; var buildingName = taskAllocation.WorkItem.WorkArea.Floor.Building.Name; var FloorName = taskAllocation.WorkItem.WorkArea.Floor.FloorName; var AreaName = taskAllocation.WorkItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewTaskRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); teamMembers.Add(taskAllocation.AssignedBy); var data = new Dictionary() { { "Keyword", "Report_Task" }, { "ProjectId", projectId.ToString() }, { "TaskAllocationId", taskAllocationId.ToString() } }; // List of device registration tokens to send the message to var registrationTokensForNotificationTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => teamMembers.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); var notificationFirebase = new Notification { Title = $"Task Approved - {project?.ProjectName}", Body = $"{name} approved your {taskAllocation.ReportedTask} of {taskAllocation.CompletedTask} tasks at {location}." }; await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); }); var registrationTokensForDataTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync(); await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data); }); await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } // Project Controller public async Task SendMessageToMultipleDevicesWithDataAsync(List registrationTokens, Notification notificationFirebase, Dictionary data) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { var message = new MulticastMessage() { Tokens = registrationTokens, Data = data, Notification = notificationFirebase }; // Send the multicast message var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message); _logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount); if (response.FailureCount > 0) { var failedTokens = new List(); for (int i = 0; i < response.Responses.Count; i++) { if (!response.Responses[i].IsSuccess) { failedTokens.Add(registrationTokens[i]); } } _logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens)); } } catch (FirebaseMessagingException ex) { // Log the specific Firebase error. _logger.LogError(ex, "Error sending push notification"); // Check for specific error codes that indicate an invalid or unregistered token. if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered || ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument) { _logger.LogWarning("FCM token is invalid and should be deleted from the database"); // TODO: Implement the logic here to remove the invalid token from your database. // Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken); } } catch (Exception ex) { _logger.LogError(ex, "Exception Occured while sending notification to firebase"); } } public async Task SendMessageToMultipleDevicesOnlyDataAsync(List registrationTokens, Dictionary data) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { var message = new MulticastMessage() { Tokens = registrationTokens, Data = data }; // Send the multicast message var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message); _logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount); if (response.FailureCount > 0) { var failedTokens = new List(); for (int i = 0; i < response.Responses.Count; i++) { if (!response.Responses[i].IsSuccess) { failedTokens.Add(registrationTokens[i]); } } _logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens)); } } catch (FirebaseMessagingException ex) { // Log the specific Firebase error. _logger.LogError(ex, "Error sending push notification"); // Check for specific error codes that indicate an invalid or unregistered token. if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered || ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument) { _logger.LogWarning("FCM token is invalid and should be deleted from the database"); // TODO: Implement the logic here to remove the invalid token from your database. // Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken); } } catch (Exception ex) { _logger.LogError(ex, "Exception Occured while sending notification to firebase"); } } public async Task SendMessageToMultipleDevicesAsync(List registrationTokens, Notification notificationFirebase) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { var message = new MulticastMessage() { Tokens = registrationTokens, Notification = notificationFirebase }; // Send the multicast message var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message); _logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount); if (response.FailureCount > 0) { var failedTokens = new List(); for (int i = 0; i < response.Responses.Count; i++) { if (!response.Responses[i].IsSuccess) { failedTokens.Add(registrationTokens[i]); } } _logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens)); } } catch (FirebaseMessagingException ex) { // Log the specific Firebase error. _logger.LogError(ex, "Error sending push notification"); // Check for specific error codes that indicate an invalid or unregistered token. if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered || ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument) { _logger.LogWarning("FCM token is invalid and should be deleted from the database"); // TODO: Implement the logic here to remove the invalid token from your database. // Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken); } } } } }