Refactor CartService to use async methods for cart operations and integrate gRPC for backend synchronization

This commit is contained in:
masoodafar-web
2025-11-20 20:10:17 +03:30
parent ddb47fda08
commit 30bac23114
7 changed files with 107 additions and 14 deletions

View File

@@ -1,3 +1,6 @@
using FrontOffice.BFF.ShopingCart.Protobuf.Protos.ShopingCart;
using Google.Protobuf.WellKnownTypes;
namespace FrontOffice.Main.Utilities;
public record CartItem(long ProductId, string Title, string ImageUrl, long UnitPrice, int Quantity)
@@ -7,14 +10,21 @@ public record CartItem(long ProductId, string Title, string ImageUrl, long UnitP
public class CartService
{
private readonly ShopingCartContract.ShopingCartContractClient _client;
private readonly List<CartItem> _items = new();
public event Action? OnChange;
public CartService(ShopingCartContract.ShopingCartContractClient client)
{
_client = client;
_ = LoadFromServerAsync();
}
public IReadOnlyList<CartItem> Items => _items.AsReadOnly();
public long Total => _items.Sum(i => i.LineTotal);
public int Count => _items.Sum(i => i.Quantity);
public void Add(Product product, int quantity = 1)
public async Task Add(Product product, int quantity = 1)
{
if (quantity <= 0) return;
var existing = _items.FirstOrDefault(i => i.ProductId == product.Id);
@@ -28,9 +38,22 @@ public class CartService
_items[idx] = existing with { Quantity = existing.Quantity + quantity };
}
Notify();
try
{
await _client.AddNewUserCartAsync(new AddNewUserCartRequest
{
ProductId = product.Id,
Count = quantity
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public void UpdateQuantity(long productId, int quantity)
public async Task UpdateQuantity(long productId, int quantity)
{
var existing = _items.FirstOrDefault(i => i.ProductId == productId);
if (existing is null) return;
@@ -42,20 +65,87 @@ public class CartService
var idx = _items.IndexOf(existing);
_items[idx] = existing with { Quantity = quantity };
Notify();
try
{
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = productId,
Count = quantity
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public void Remove(long productId)
public async Task Remove(long productId)
{
_items.RemoveAll(i => i.ProductId == productId);
Notify();
try
{
// Interpret Count=0 as remove from cart
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = productId,
Count = 0
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public void Clear()
public async Task Clear()
{
var productIds = _items.Select(i => i.ProductId).ToList();
_items.Clear();
Notify();
try
{
foreach (var id in productIds)
{
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = id,
Count = 0
});
}
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
private void Notify() => OnChange?.Invoke();
}
private async Task LoadFromServerAsync()
{
try
{
var response = await _client.GetAllUserCartAsync(new Empty());
_items.Clear();
foreach (var m in response.Models)
{
var item = new CartItem(
ProductId: m.Id,
Title: m.Title ?? string.Empty,
ImageUrl: string.IsNullOrWhiteSpace(m.ImagePath) ? string.Empty : UrlUtility.DownloadUrl + m.ImagePath,
UnitPrice: m.Price,
Quantity: m.SaleCount > 0 ? m.SaleCount : 1);
_items.Add(item);
}
Notify();
}
catch
{
// If backend is unreachable or user is unauthenticated, fall back to local-only cart.
}
}
}