using FirebaseAdmin.Messaging; using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Expenses; using Marco.Pms.Model.Projects; 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 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 teamMemberTask = 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.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 = 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 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 teamMemberTask = 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 //Project Infra public async Task SendModifyTaskMeaasgeAsync(List workItemIds, string name, bool IsExist, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var workItems = await _context.WorkItems .Include(wi => wi.ActivityMaster) .Include(wi => wi.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .Where(wi => workItemIds.Contains(wi.Id) && wi.TenantId == tenantId && wi.ActivityMaster != null && wi.WorkArea != null && wi.WorkArea.Floor != null && wi.WorkArea.Floor.Building != null) .ToListAsync(); if (!workItems.Any()) { return; } var workItem = workItems.FirstOrDefault(); 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 viewProjectInfraRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewProjectInfra) .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, viewProjectInfraRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var viewProjectInfraRoleIds = viewProjectInfraRoleTask.Result; var project = projectTask.Result; var activityName = workItem.ActivityMaster!.ActivityName; var buildingName = workItem.WorkArea.Floor.Building.Name; var FloorName = workItem.WorkArea.Floor.FloorName; var AreaName = workItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; var buildingId = workItem.WorkArea.Floor.Building.Id; var floorId = workItem.WorkArea.Floor.Id; var areaId = workItem.WorkArea.Id; 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) || viewProjectInfraRoleIds.Contains(er.RoleId))) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase; if (IsExist) { notificationFirebase = new Notification { Title = $"Task Updated - {project?.ProjectName}", Body = $"{name} updated tasks {activityName} at {location}." }; } else { notificationFirebase = new Notification { Title = $"New Task Created - {project?.ProjectName}", Body = $"{name} created tasks {activityName} at {location}." }; } var data = new Dictionary() { { "Keyword", "Task_Modified" }, { "ProjectId", projectId.ToString() }, { "BuildingId", buildingId.ToString() }, { "FloorId", floorId.ToString() }, { "AreaId", areaId.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendModifyWorkAreaMeaasgeAsync(Guid workAreaId, string name, bool IsExist, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var workArea = await _context.WorkAreas .Include(wa => wa.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(wa => wa.Id == workAreaId && wa.TenantId == tenantId && wa.Floor != null && wa.Floor.Building != null); if (workArea == null) { return; } var projectId = 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 viewProjectInfraRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewProjectInfra) .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, manageProjectsRoleTask, viewProjectInfraRoleTask); var manageProjectsRoleIds = manageProjectsRoleTask.Result; var viewProjectInfraRoleIds = viewProjectInfraRoleTask.Result; var project = projectTask.Result; var buildingName = workArea.Floor.Building.Name; var FloorName = workArea.Floor.FloorName; var location = $"{buildingName} > {FloorName}"; var buildingId = workArea.Floor.Building.Id; var floorId = workArea.Floor.Id; var areaName = workArea.AreaName; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewProjectInfraRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase; if (IsExist) { notificationFirebase = new Notification { Title = $"WorkArea Updated - {project?.ProjectName}", Body = $"{name} updated WorkArea \"{areaName}\" at {location}." }; } else { notificationFirebase = new Notification { Title = $"New WorkArea Created - {project?.ProjectName}", Body = $"{name} created WorkArea \"{areaName}\" at {location}." }; } var data = new Dictionary() { { "Keyword", "WorkArea_Modified" }, { "ProjectId", projectId.ToString() }, { "BuildingId", buildingId.ToString() }, { "FloorId", floorId.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendModifyFloorMeaasgeAsync(Guid floorId, string name, bool IsExist, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var floor = await _context.Floor .Include(f => f.Building) .FirstOrDefaultAsync(f => f.Id == floorId && f.TenantId == tenantId && f.Building != null); if (floor == null) { return; } var projectId = 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 viewProjectInfraRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewProjectInfra) .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, manageProjectsRoleTask, viewProjectInfraRoleTask); var manageProjectsRoleIds = manageProjectsRoleTask.Result; var viewProjectInfraRoleIds = viewProjectInfraRoleTask.Result; var project = projectTask.Result; var buildingName = floor.Building.Name; var floorName = floor.FloorName; var buildingId = floor.Building.Id; List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId)) && viewProjectInfraRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase; if (IsExist) { notificationFirebase = new Notification { Title = $"Floor Updated - {project?.ProjectName}", Body = $"{name} updated Floor \"{floorName}\" at {buildingName}." }; } else { notificationFirebase = new Notification { Title = $"New Floor Created - {project?.ProjectName}", Body = $"{name} created Floor \"{floorName}\" at {buildingName}." }; } var data = new Dictionary() { { "Keyword", "Floor_Modified" }, { "ProjectId", projectId.ToString() }, { "BuildingId", buildingId.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendModifyBuildingMeaasgeAsync(Guid buildingId, string name, bool IsExist, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); var building = await _context.Buildings .FirstOrDefaultAsync(b => b.Id == buildingId && b.TenantId == tenantId); if (building == null) { return; } var projectId = 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 viewProjectInfraRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewProjectInfra) .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, manageProjectsRoleTask, viewProjectInfraRoleTask); var manageProjectsRoleIds = manageProjectsRoleTask.Result; var viewProjectInfraRoleIds = viewProjectInfraRoleTask.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)) && viewProjectInfraRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); var buildingName = building.Name; Notification notificationFirebase; if (IsExist) { notificationFirebase = new Notification { Title = $"Building Updated - {project?.ProjectName}", Body = $"{name} updated building \"{buildingName}\" in {project?.ProjectName}." }; } else { notificationFirebase = new Notification { Title = $"New Building Created - {project?.ProjectName}", Body = $"\"{{name}} created a new building \"{buildingName}\" in {project?.ProjectName}." }; } var data = new Dictionary() { { "Keyword", "Building_Modified" }, { "ProjectId", projectId.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } public async Task SendDeleteTaskMeaasgeAsync(Guid workItemId, string name, 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.ActivityMaster) .Include(wi => wi.WorkArea) .ThenInclude(wa => wa!.Floor) .ThenInclude(f => f!.Building) .FirstOrDefaultAsync(wi => workItemId == wi.Id && wi.TenantId == tenantId && wi.ActivityMaster != null && 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 viewProjectInfraRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewProjectInfra) .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, viewProjectInfraRoleTask); var viewTaskRoleIds = viewTaskRoleTask.Result; var manageProjectsRoleIds = manageProjectsRoleTask.Result; var viewProjectInfraRoleIds = viewProjectInfraRoleTask.Result; var project = projectTask.Result; var activityName = workItem.ActivityMaster!.ActivityName; var buildingName = workItem.WorkArea.Floor.Building.Name; var FloorName = workItem.WorkArea.Floor.FloorName; var AreaName = workItem.WorkArea.AreaName; var location = $"{buildingName} > {FloorName} > {AreaName}"; var buildingId = workItem.WorkArea.Floor.Building.Id; var floorId = workItem.WorkArea.Floor.Id; var areaId = workItem.WorkArea.Id; 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) || viewProjectInfraRoleIds.Contains(er.RoleId))) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase = new Notification { Title = $"Task Deleted - {project?.ProjectName}", Body = $"{name} deleted tasks {activityName} at {location}." }; var data = new Dictionary() { { "Keyword", "Task_Modified" }, { "ProjectId", projectId.ToString() }, { "BuildingId", buildingId.ToString() }, { "FloorId", floorId.ToString() }, { "AreaId", areaId.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } //Project Allocation public async Task SendProjectAllocationMessageAsync(List projectAllocations, string name, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { await using var _context = await _dbContextFactory.CreateDbContextAsync(); foreach (var projectAllocation in projectAllocations) { var projectId = projectAllocation.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 manageTeamRoleTask = Task.Run(async () => { await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.RolePermissionMappings .Where(rp => rp.FeaturePermissionId == PermissionsMaster.ManageTeam) .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, manageTeamRoleTask, manageProjectsRoleTask); var manageTeamRoleIds = manageTeamRoleTask.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)) && manageTeamRoleIds.Contains(er.RoleId)) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase; if (projectAllocation.IsActive) { notificationFirebase = new Notification { Title = $"Assigned to Project - {project?.ProjectName}", Body = $"You have been assigned to the project \"{project?.ProjectName}\"." }; } else { notificationFirebase = new Notification { Title = $"Removed from Project - {project?.ProjectName}", Body = $"{name} has removed you from the project \"{project?.ProjectName}\"." }; } var data = new Dictionary() { { "Keyword", "Team_Modefied" }, { "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 => projectAllocation.EmployeeId == ft.EmployeeId).Select(ft => ft.FcmToken).ToListAsync(); 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 Management public async Task SendModifyProjectMessageAsync(Project project, string name, bool IsExist, Guid tenantId) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { 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 == project.Id && 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 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, manageProjectsRoleTask); var manageProjectsRoleIds = manageProjectsRoleTask.Result; var projectVM = projectTask.Result; List projectAssignedEmployeeIds = projectVM?.EmployeeIds ?? new List(); var employeeIds = await _context.EmployeeRoleMappings .Where(er => (projectAssignedEmployeeIds.Contains(er.EmployeeId) || manageProjectsRoleIds.Contains(er.RoleId))) .Select(er => er.EmployeeId) .ToListAsync(); Notification notificationFirebase; if (IsExist) { notificationFirebase = new Notification { Title = $"Project Updated - {project.Name}", Body = $"{name} updated the project \"{project.Name}\"." }; } else { notificationFirebase = new Notification { Title = $"Project Created - {project.Name}", Body = $"A new project \"{project.Name}\" has been created by {name}." }; } var data = new Dictionary() { { "Keyword", "Project_Modefied" }, { "ProjectId", project.Id.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(); await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data); } catch (Exception ex) { _logger.LogError(ex, "Exception occured while get data for sending notification"); } } //Contact Controller public async Task SendReviewExpenseMessageAsync(Expenses expenses, string name, bool IsExist, Guid tenantId) { } #region =================================================================== Helper Functions =================================================================== public async Task SendMessageToMultipleDevicesWithDataAsync(List registrationTokens, Notification notificationFirebase, Dictionary data) { using var scope = _serviceScopeFactory.CreateScope(); var _logger = scope.ServiceProvider.GetRequiredService(); try { registrationTokens = registrationTokens.Distinct().ToList(); 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 { registrationTokens = registrationTokens.Distinct().ToList(); 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 { registrationTokens = registrationTokens.Distinct().ToList(); 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); } } } #endregion } }