Migliorata gestione e visualizzazione notifiche

This commit is contained in:
2025-09-12 15:42:56 +02:00
parent b798b01da0
commit 223e74c490
20 changed files with 229 additions and 60 deletions

View File

@@ -10,6 +10,7 @@ using salesbook.Shared.Core.Interface.IntegryApi;
using salesbook.Shared.Core.Interface.System.Network; using salesbook.Shared.Core.Interface.System.Network;
using Sentry.Protocol; using Sentry.Protocol;
using System.Linq.Expressions; using System.Linq.Expressions;
using salesbook.Shared.Core.Helpers;
namespace salesbook.Maui.Core.Services; namespace salesbook.Maui.Core.Services;
@@ -159,6 +160,35 @@ public class ManageDataService(
} }
} }
public async Task<List<ActivityDTO>> GetActivityTryLocalDb(WhereCondActivity whereCond)
{
List<StbActivity>? activities;
activities = await localDb.Get<StbActivity>(x =>
(whereCond.ActivityId != null && x.ActivityId != null && whereCond.ActivityId.Equals(x.ActivityId)) ||
(whereCond.Start != null && whereCond.End != null && x.EffectiveDate == null &&
x.EstimatedDate >= whereCond.Start && x.EstimatedDate <= whereCond.End) ||
(x.EffectiveDate >= whereCond.Start && x.EffectiveDate <= whereCond.End) ||
(whereCond.ActivityId == null && (whereCond.Start == null || whereCond.End == null))
);
if (activities.IsNullOrEmpty() && networkService.ConnectionAvailable)
{
activities = await integryApiService.RetrieveActivity(
new CRMRetrieveActivityRequestDTO
{
StarDate = whereCond.Start,
EndDate = whereCond.End,
ActivityId = whereCond.ActivityId
}
);
_ = UpdateDb(activities);
}
return await MapActivity(activities);
}
public async Task<List<ActivityDTO>> GetActivity(WhereCondActivity whereCond, bool useLocalDb) public async Task<List<ActivityDTO>> GetActivity(WhereCondActivity whereCond, bool useLocalDb)
{ {
List<StbActivity>? activities; List<StbActivity>? activities;
@@ -187,6 +217,11 @@ public class ManageDataService(
); );
} }
return await MapActivity(activities);
}
private async Task<List<ActivityDTO>> MapActivity(List<StbActivity>? activities)
{
if (activities == null) return []; if (activities == null) return [];
var codJcomList = activities var codJcomList = activities

View File

@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using salesbook.Shared.Core.Entity; using salesbook.Shared.Core.Entity;
using salesbook.Shared.Core.Interface.IntegryApi; using salesbook.Shared.Core.Interface.IntegryApi;
using salesbook.Shared.Core.Messages.Notification; using salesbook.Shared.Core.Messages.Notification.NewPush;
using Shiny.Push; using Shiny.Push;
namespace salesbook.Maui.Core.System.Notification.Push; namespace salesbook.Maui.Core.System.Notification.Push;

View File

@@ -22,7 +22,8 @@ using salesbook.Shared.Core.Messages.Activity.Copy;
using salesbook.Shared.Core.Messages.Activity.New; using salesbook.Shared.Core.Messages.Activity.New;
using salesbook.Shared.Core.Messages.Back; using salesbook.Shared.Core.Messages.Back;
using salesbook.Shared.Core.Messages.Contact; using salesbook.Shared.Core.Messages.Contact;
using salesbook.Shared.Core.Messages.Notification; using salesbook.Shared.Core.Messages.Notification.Loaded;
using salesbook.Shared.Core.Messages.Notification.NewPush;
using salesbook.Shared.Core.Services; using salesbook.Shared.Core.Services;
using Shiny; using Shiny;
@@ -81,8 +82,9 @@ namespace salesbook.Maui
builder.Services.AddSingleton<BackNavigationService>(); builder.Services.AddSingleton<BackNavigationService>();
builder.Services.AddSingleton<CopyActivityService>(); builder.Services.AddSingleton<CopyActivityService>();
builder.Services.AddSingleton<NewContactService>(); builder.Services.AddSingleton<NewContactService>();
builder.Services.AddSingleton<NotificationsLoadedService>();
builder.Services.AddSingleton<NewPushNotificationService>(); builder.Services.AddSingleton<NewPushNotificationService>();
//Notification //Notification
builder.Services.AddNotifications(); builder.Services.AddNotifications();
builder.Services.AddPush<PushNotificationDelegate>(); builder.Services.AddPush<PushNotificationDelegate>();
@@ -90,6 +92,7 @@ namespace salesbook.Maui
builder.Services.AddSingleton<IIntegryNotificationRestClient, IntegryNotificationRestClient>(); builder.Services.AddSingleton<IIntegryNotificationRestClient, IntegryNotificationRestClient>();
builder.Services.AddSingleton<IFirebaseNotificationService, FirebaseNotificationService>(); builder.Services.AddSingleton<IFirebaseNotificationService, FirebaseNotificationService>();
builder.Services.AddSingleton<IShinyNotificationManager, ShinyNotificationManager>(); builder.Services.AddSingleton<IShinyNotificationManager, ShinyNotificationManager>();
builder.Services.AddSingleton<INotificationService, NotificationService>();
#if DEBUG #if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools(); builder.Services.AddBlazorWebViewDeveloperTools();

View File

@@ -6,12 +6,14 @@
@using salesbook.Shared.Core.Messages.Activity.Copy @using salesbook.Shared.Core.Messages.Activity.Copy
@using salesbook.Shared.Core.Messages.Activity.New @using salesbook.Shared.Core.Messages.Activity.New
@using salesbook.Shared.Core.Messages.Contact @using salesbook.Shared.Core.Messages.Contact
@using salesbook.Shared.Core.Messages.Notification @using salesbook.Shared.Core.Messages.Notification.Loaded
@using salesbook.Shared.Core.Messages.Notification.NewPush
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IMessenger Messenger @inject IMessenger Messenger
@inject CopyActivityService CopyActivityService @inject CopyActivityService CopyActivityService
@inject NewPushNotificationService NewPushNotificationService @inject NewPushNotificationService NewPushNotificationService
@inject NotificationState Notification @inject NotificationState Notification
@inject NotificationsLoadedService NotificationsLoadedService
<div class="container animated-navbar @(IsVisible ? "show-nav" : "hide-nav") @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")"> <div class="container animated-navbar @(IsVisible ? "show-nav" : "hide-nav") @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")">
<nav class="navbar @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")"> <nav class="navbar @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")">
@@ -68,8 +70,7 @@
protected override Task OnInitializedAsync() protected override Task OnInitializedAsync()
{ {
CopyActivityService.OnCopyActivity += async dto => await CreateActivity(dto); InitMessage();
NewPushNotificationService.OnNotificationReceived += NewNotificationReceived;
NavigationManager.LocationChanged += (_, args) => NavigationManager.LocationChanged += (_, args) =>
{ {
@@ -117,4 +118,11 @@
Notification.ReceivedNotifications.Add(notification); Notification.ReceivedNotifications.Add(notification);
InvokeAsync(StateHasChanged); InvokeAsync(StateHasChanged);
} }
private void InitMessage()
{
CopyActivityService.OnCopyActivity += async dto => await CreateActivity(dto);
NewPushNotificationService.OnNotificationReceived += NewNotificationReceived;
NotificationsLoadedService.OnNotificationsLoaded += () => InvokeAsync(StateHasChanged);
}
} }

View File

@@ -0,0 +1,12 @@
<MudOverlay Visible="Visible" DarkBackground="false">
<div class="overlay-container">
<span>Caricamento</span>
<MudProgressLinear Color="Color.Primary" Rounded="true" Size="Size.Medium" Indeterminate="true" />
</div>
</MudOverlay>
@code
{
[Parameter] public bool Visible { get; set; }
}

View File

@@ -0,0 +1,18 @@
.overlay-container {
background: var(--mud-palette-background);
width: 20rem;
height: 6rem;
padding: 0 1rem;
display: flex;
gap: .5rem;
flex-direction: column;
justify-content: center;
border-radius: 20px;
box-shadow: var(--custom-box-shadow);
}
.overlay-container > span {
text-align: center;
font-size: 20px;
font-weight: 600;
}

View File

@@ -1,15 +1,19 @@
@page "/" @page "/"
@attribute [Authorize] @attribute [Authorize]
@using CommunityToolkit.Mvvm.Messaging
@using salesbook.Shared.Core.Interface @using salesbook.Shared.Core.Interface
@using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Core.Interface.System.Network @using salesbook.Shared.Core.Interface.System.Network
@using salesbook.Shared.Core.Interface.System.Notification @using salesbook.Shared.Core.Interface.System.Notification
@using salesbook.Shared.Core.Messages.Notification.Loaded
@using salesbook.Shared.Core.Services @using salesbook.Shared.Core.Services
@inject IFormFactor FormFactor @inject IFormFactor FormFactor
@inject INetworkService NetworkService @inject INetworkService NetworkService
@inject IFirebaseNotificationService FirebaseNotificationService @inject IFirebaseNotificationService FirebaseNotificationService
@inject IShinyNotificationManager NotificationManager @inject IShinyNotificationManager NotificationManager
@inject INotificationService NotificationService
@inject PreloadService PreloadService @inject PreloadService PreloadService
@inject IMessenger Messenger
<SpinnerLayout FullScreen="true" /> <SpinnerLayout FullScreen="true" />
@@ -17,6 +21,7 @@
{ {
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await LoadNotification();
await CheckAndRequestPermissions(); await CheckAndRequestPermissions();
try try
@@ -40,6 +45,12 @@
NavigationManager.NavigateTo("/Calendar"); NavigationManager.NavigateTo("/Calendar");
} }
private async Task LoadNotification()
{
await NotificationService.LoadNotification();
Messenger.Send(new NotificationsLoadedMessage());
}
private async Task CheckAndRequestPermissions() private async Task CheckAndRequestPermissions()
{ {
await NotificationManager.RequestAccess(); await NotificationManager.RequestAccess();

View File

@@ -1,20 +1,25 @@
@page "/Notifications" @page "/Notifications"
@attribute [Authorize] @attribute [Authorize]
@using CommunityToolkit.Mvvm.Messaging
@using salesbook.Shared.Components.Layout @using salesbook.Shared.Components.Layout
@using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Components.SingleElements @using salesbook.Shared.Components.SingleElements
@using salesbook.Shared.Core.Dto.PageState @using salesbook.Shared.Core.Dto.PageState
@using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Entity
@using salesbook.Shared.Core.Interface
@using salesbook.Shared.Core.Interface.IntegryApi @using salesbook.Shared.Core.Interface.IntegryApi
@using salesbook.Shared.Core.Messages.Notification @using salesbook.Shared.Core.Messages.Notification.Loaded
@using salesbook.Shared.Core.Messages.Notification.NewPush
@inject NotificationState Notification @inject NotificationState Notification
@inject NewPushNotificationService NewPushNotificationService @inject NewPushNotificationService NewPushNotificationService
@inject IJSRuntime JS @inject IJSRuntime JS
@inject IIntegryNotificationRestClient IntegryNotificationRestClient @inject IIntegryNotificationRestClient IntegryNotificationRestClient
@inject INotificationService NotificationService
@inject IMessenger Messenger
<HeaderLayout Title="Notifiche" /> <HeaderLayout Title="Notifiche" />
<div class="container"> <div class="container container-notifications">
@if (Loading) @if (Loading)
{ {
<SpinnerLayout FullScreen="true" /> <SpinnerLayout FullScreen="true" />
@@ -50,7 +55,7 @@
@code { @code {
private DotNetObjectReference<Notifications>? _objectReference; private DotNetObjectReference<Notifications>? _objectReference;
private bool Loading { get; set; } = true; private bool Loading { get; set; }
protected override Task OnInitializedAsync() protected override Task OnInitializedAsync()
{ {
@@ -62,37 +67,6 @@
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
await JS.InvokeVoidAsync("initNotifications", _objectReference); await JS.InvokeVoidAsync("initNotifications", _objectReference);
if (firstRender)
{
await LoadData();
Loading = false;
StateHasChanged();
}
}
private async Task LoadData()
{
var allNotifications = await IntegryNotificationRestClient.Get();
var allIds = allNotifications.Select(n => n.Id).ToHashSet();
Notification.ReceivedNotifications = Notification.ReceivedNotifications
.Where(r => !allIds.Contains(r.Id))
.ToList();
Notification.UnreadNotifications = allNotifications
.Where(x =>
x.WtbDeviceNotifications == null ||
x.WtbDeviceNotifications.Any(y => y.ReadDate == null))
.ToList();
Notification.NotificationsRead = allNotifications
.Where(x =>
x.WtbDeviceNotifications != null &&
x.WtbDeviceNotifications.All(y => y.ReadDate != null))
.ToList();
OrderNotificationList();
} }
private void NewNotificationReceived(WtbNotification notification) private void NewNotificationReceived(WtbNotification notification)
@@ -119,7 +93,7 @@
if (!removed) return; if (!removed) return;
OrderNotificationList(); NotificationService.OrderNotificationList();
Loading = false; Loading = false;
_ = InvokeAsync(StateHasChanged); _ = InvokeAsync(StateHasChanged);
@@ -127,6 +101,8 @@
{ {
_ = IntegryNotificationRestClient.Delete(notificationId); _ = IntegryNotificationRestClient.Delete(notificationId);
}); });
Messenger.Send(new NotificationsLoadedMessage());
} }
[JSInvokable] [JSInvokable]
@@ -155,23 +131,12 @@
wtbNotification = await IntegryNotificationRestClient.MarkAsRead(notificationId); wtbNotification = await IntegryNotificationRestClient.MarkAsRead(notificationId);
Notification.NotificationsRead.Add(wtbNotification); Notification.NotificationsRead.Add(wtbNotification);
OrderNotificationList(); NotificationService.OrderNotificationList();
Messenger.Send(new NotificationsLoadedMessage());
Loading = false; Loading = false;
StateHasChanged(); StateHasChanged();
} }
private void OrderNotificationList()
{
Notification.ReceivedNotifications = Notification.ReceivedNotifications
.OrderByDescending(x => x.StartDate).ToList();
Notification.UnreadNotifications = Notification.UnreadNotifications
.OrderByDescending(x => x.StartDate).ToList();
Notification.NotificationsRead = Notification.NotificationsRead
.OrderByDescending(x => x.StartDate).ToList();
}
public void Dispose() public void Dispose()
{ {
_objectReference?.Dispose(); _objectReference?.Dispose();

View File

@@ -1,6 +1,21 @@
.container-notifications {
height: 100%;
overflow: auto;
padding: .2rem 0 75px 0;
}
.list { .list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
} }
.container-notifications::-webkit-scrollbar {
width: 6px;
}
.container-notifications::-webkit-scrollbar-thumb {
background: #bbb;
border-radius: 3px;
}

View File

@@ -1,6 +1,5 @@
@using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Core.Dto @using salesbook.Shared.Core.Dto
@using salesbook.Shared.Core.Interface
@using salesbook.Shared.Components.Layout.Overlay @using salesbook.Shared.Components.Layout.Overlay
@using salesbook.Shared.Core.Interface.IntegryApi @using salesbook.Shared.Core.Interface.IntegryApi
@inject IIntegryApiService IntegryApiService @inject IIntegryApiService IntegryApiService

View File

@@ -1,6 +1,12 @@
@using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Core.Dto.Activity
@using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Entity
@using salesbook.Shared.Core.Interface
@inject IManageDataService ManageDataService
@inject ISnackbar Snackbar
@inject IDialogService Dialog
<div class="row" id="@Notification.Id"> <div class="row" id="@Notification.Id" @onclick="OpenActivity">
<div class="behind-left"> <div class="behind-left">
<button class="read-btn"> <button class="read-btn">
<MudIcon Icon="@Icons.Material.Rounded.Check" /> <MudIcon Icon="@Icons.Material.Rounded.Check" />
@@ -51,10 +57,35 @@
</div> </div>
</div> </div>
<OverlayLayout Visible="VisibleOverlay" />
@code { @code {
[Parameter] public bool Unread { get; set; } [Parameter] public bool Unread { get; set; }
[Parameter] public WtbNotification Notification { get; set; } = new(); [Parameter] public WtbNotification Notification { get; set; } = new();
private bool VisibleOverlay { get; set; }
private async Task OpenActivity()
{
if(Notification.NotificationData?.ActivityId == null) return;
var activityId = Notification.NotificationData.ActivityId;
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
Snackbar.Clear();
VisibleOverlay = true;
StateHasChanged();
var activity = (await ManageDataService.GetActivityTryLocalDb(new WhereCondActivity { ActivityId = activityId })).LastOrDefault();
VisibleOverlay = false;
StateHasChanged();
if (activity == null) Snackbar.Add("Impossibile aprire l'attivit<69>", Severity.Error);
_ = ModalHelpers.OpenActivityForm(Dialog, activity, null);
}
private static string GetTimeAgo(DateTime? timestamp) private static string GetTimeAgo(DateTime? timestamp)
{ {
if (timestamp is null) return ""; if (timestamp is null) return "";
@@ -77,7 +108,7 @@
return $"{timestamp.Value:t}"; return $"{timestamp.Value:t}";
default: default:
{ {
return difference.TotalDays < 7 ? $"{(int)difference.TotalDays}g fa" : timestamp.Value.ToString("dd/MM/yyyy"); return timestamp.Value.ToString("dd/MM/yyyy");
} }
} }
} }

View File

@@ -15,6 +15,7 @@ public interface IManageDataService
Task<List<ContactDTO>> GetContact(WhereCondContact whereCond); Task<List<ContactDTO>> GetContact(WhereCondContact whereCond);
Task<ContactDTO?> GetSpecificContact(string codAnag, bool IsContact); Task<ContactDTO?> GetSpecificContact(string codAnag, bool IsContact);
Task<List<ActivityDTO>> GetActivityTryLocalDb(WhereCondActivity whereCond);
Task<List<ActivityDTO>> GetActivity(WhereCondActivity whereCond, bool useLocalDb = false); Task<List<ActivityDTO>> GetActivity(WhereCondActivity whereCond, bool useLocalDb = false);
Task InsertOrUpdate<T>(T objectToSave); Task InsertOrUpdate<T>(T objectToSave);

View File

@@ -0,0 +1,7 @@
namespace salesbook.Shared.Core.Interface;
public interface INotificationService
{
Task LoadNotification();
void OrderNotificationList();
}

View File

@@ -0,0 +1,5 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace salesbook.Shared.Core.Messages.Notification.Loaded;
public class NotificationsLoadedMessage(object? value = null) : ValueChangedMessage<object?>(value);

View File

@@ -0,0 +1,13 @@
using CommunityToolkit.Mvvm.Messaging;
namespace salesbook.Shared.Core.Messages.Notification.Loaded;
public class NotificationsLoadedService
{
public event Action? OnNotificationsLoaded;
public NotificationsLoadedService(IMessenger messenger)
{
messenger.Register<NotificationsLoadedMessage>(this, (_, _) => { OnNotificationsLoaded?.Invoke(); });
}
}

View File

@@ -1,6 +1,6 @@
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using salesbook.Shared.Core.Entity; using salesbook.Shared.Core.Entity;
namespace salesbook.Shared.Core.Messages.Notification; namespace salesbook.Shared.Core.Messages.Notification.NewPush;
public class NewPushNotificationMessage(WtbNotification value) : ValueChangedMessage<WtbNotification>(value); public class NewPushNotificationMessage(WtbNotification value) : ValueChangedMessage<WtbNotification>(value);

View File

@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using salesbook.Shared.Core.Entity; using salesbook.Shared.Core.Entity;
namespace salesbook.Shared.Core.Messages.Notification; namespace salesbook.Shared.Core.Messages.Notification.NewPush;
public class NewPushNotificationService public class NewPushNotificationService
{ {

View File

@@ -0,0 +1,45 @@
using salesbook.Shared.Core.Dto.PageState;
using salesbook.Shared.Core.Interface;
using salesbook.Shared.Core.Interface.IntegryApi;
namespace salesbook.Shared.Core.Services;
public class NotificationService(
IIntegryNotificationRestClient integryNotificationRestClient,
NotificationState Notification
) : INotificationService
{
public async Task LoadNotification()
{
var allNotifications = await integryNotificationRestClient.Get();
var allIds = allNotifications.Select(n => n.Id).ToHashSet();
Notification.ReceivedNotifications = Notification.ReceivedNotifications
.Where(r => !allIds.Contains(r.Id))
.ToList();
Notification.UnreadNotifications = allNotifications
.Where(x =>
x.WtbDeviceNotifications == null ||
x.WtbDeviceNotifications.Any(y => y.ReadDate == null))
.ToList();
Notification.NotificationsRead = allNotifications
.Where(x =>
x.WtbDeviceNotifications != null &&
x.WtbDeviceNotifications.All(y => y.ReadDate != null))
.ToList();
}
public void OrderNotificationList()
{
Notification.ReceivedNotifications = Notification.ReceivedNotifications
.OrderByDescending(x => x.StartDate).ToList();
Notification.UnreadNotifications = Notification.UnreadNotifications
.OrderByDescending(x => x.StartDate).ToList();
Notification.NotificationsRead = Notification.NotificationsRead
.OrderByDescending(x => x.StartDate).ToList();
}
}

View File

@@ -28,6 +28,7 @@ article {
} }
/*ServicesIsDown" : "SystemOk" : "NetworkKo*/ /*ServicesIsDown" : "SystemOk" : "NetworkKo*/
.Connection { .Connection {
padding: 0 .75rem; padding: 0 .75rem;
font-weight: 700; font-weight: 700;

View File

@@ -5,7 +5,7 @@
/*Utility*/ /*Utility*/
--exception-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.3); --exception-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.3);
--custom-box-shadow: 1px 2px 5px var(--gray-for-shadow); --custom-box-shadow: 1px 2px 5px var(--gray-for-shadow);
--mud-default-borderradius: 12px !important; --mud-default-borderradius: 20px !important;
--m-page-x: 1rem; --m-page-x: 1rem;
--mh-header: 4rem; --mh-header: 4rem;
} }