mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 05:54:26 +01:00
Greenfield: Add store id for notifications (#6093)
* Rename filter to storeid for consistency with other filters * Greenfield: Add storeId to notification * Cleanups * Greenfield: Allow filtering notifications by store id
This commit is contained in:
@@ -9,7 +9,7 @@ namespace BTCPayServer.Client;
|
|||||||
public partial class BTCPayServerClient
|
public partial class BTCPayServerClient
|
||||||
{
|
{
|
||||||
public virtual async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null, int? skip = null,
|
public virtual async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null, int? skip = null,
|
||||||
int? take = null, CancellationToken token = default)
|
int? take = null, string[] storeId = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var queryPayload = new Dictionary<string, object>();
|
var queryPayload = new Dictionary<string, object>();
|
||||||
if (seen != null)
|
if (seen != null)
|
||||||
@@ -18,6 +18,8 @@ public partial class BTCPayServerClient
|
|||||||
queryPayload.Add(nameof(skip), skip);
|
queryPayload.Add(nameof(skip), skip);
|
||||||
if (take != null)
|
if (take != null)
|
||||||
queryPayload.Add(nameof(take), take);
|
queryPayload.Add(nameof(take), take);
|
||||||
|
if (storeId != null)
|
||||||
|
queryPayload.Add(nameof(storeId), storeId);
|
||||||
return await SendHttpRequest<IEnumerable<NotificationData>>("api/v1/users/me/notifications", queryPayload, HttpMethod.Get, token);
|
return await SendHttpRequest<IEnumerable<NotificationData>>("api/v1/users/me/notifications", queryPayload, HttpMethod.Get, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace BTCPayServer.Client.Models
|
|||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public string Body { get; set; }
|
public string Body { get; set; }
|
||||||
|
public string StoreId { get; set; }
|
||||||
public bool Seen { get; set; }
|
public bool Seen { get; set; }
|
||||||
public Uri Link { get; set; }
|
public Uri Link { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -13,7 +13,6 @@ using BTCPayServer.Events;
|
|||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.NTag424;
|
using BTCPayServer.NTag424;
|
||||||
using BTCPayServer.Payments;
|
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
using BTCPayServer.PayoutProcessors;
|
using BTCPayServer.PayoutProcessors;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
@@ -30,7 +29,6 @@ using Newtonsoft.Json.Linq;
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
using Xunit.Sdk;
|
using Xunit.Sdk;
|
||||||
using static Org.BouncyCastle.Math.EC.ECCurve;
|
|
||||||
using CreateApplicationUserRequest = BTCPayServer.Client.Models.CreateApplicationUserRequest;
|
using CreateApplicationUserRequest = BTCPayServer.Client.Models.CreateApplicationUserRequest;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
@@ -2915,14 +2913,18 @@ namespace BTCPayServer.Tests
|
|||||||
await tester.PayTester.GetService<NotificationSender>()
|
await tester.PayTester.GetService<NotificationSender>()
|
||||||
.SendNotification(new UserScope(user.UserId), new NewVersionNotification());
|
.SendNotification(new UserScope(user.UserId), new NewVersionNotification());
|
||||||
|
|
||||||
Assert.Single(await viewOnlyClient.GetNotifications());
|
var notifications = (await viewOnlyClient.GetNotifications()).ToList();
|
||||||
|
Assert.Single(notifications);
|
||||||
Assert.Single(await viewOnlyClient.GetNotifications(false));
|
Assert.Single(await viewOnlyClient.GetNotifications(false));
|
||||||
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
||||||
|
|
||||||
|
var notification = notifications.First();
|
||||||
|
Assert.Null(notification.StoreId);
|
||||||
|
|
||||||
Assert.Single(await client.GetNotifications());
|
Assert.Single(await client.GetNotifications());
|
||||||
Assert.Single(await client.GetNotifications(false));
|
Assert.Single(await client.GetNotifications(false));
|
||||||
Assert.Empty(await client.GetNotifications(true));
|
Assert.Empty(await client.GetNotifications(true));
|
||||||
var notification = (await client.GetNotifications()).First();
|
notification = (await client.GetNotifications()).First();
|
||||||
notification = await client.GetNotification(notification.Id);
|
notification = await client.GetNotification(notification.Id);
|
||||||
Assert.False(notification.Seen);
|
Assert.False(notification.Seen);
|
||||||
await AssertHttpError(403, async () =>
|
await AssertHttpError(403, async () =>
|
||||||
@@ -2940,6 +2942,41 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
||||||
Assert.Empty(await viewOnlyClient.GetNotifications(false));
|
Assert.Empty(await viewOnlyClient.GetNotifications(false));
|
||||||
|
|
||||||
|
// Store association
|
||||||
|
var unrestricted = await user.CreateClient(Policies.Unrestricted);
|
||||||
|
var store1 = await unrestricted.CreateStore(new CreateStoreRequest { Name = "Store A" });
|
||||||
|
await tester.PayTester.GetService<NotificationSender>()
|
||||||
|
.SendNotification(new UserScope(user.UserId), new InviteAcceptedNotification{
|
||||||
|
UserId = user.UserId,
|
||||||
|
UserEmail = user.Email,
|
||||||
|
StoreId = store1.Id,
|
||||||
|
StoreName = store1.Name
|
||||||
|
});
|
||||||
|
notifications = (await client.GetNotifications()).ToList();
|
||||||
|
Assert.Single(notifications);
|
||||||
|
|
||||||
|
notification = notifications.First();
|
||||||
|
Assert.Equal(store1.Id, notification.StoreId);
|
||||||
|
Assert.Equal($"User {user.Email} accepted the invite to {store1.Name}.", notification.Body);
|
||||||
|
|
||||||
|
var store2 = await unrestricted.CreateStore(new CreateStoreRequest { Name = "Store B" });
|
||||||
|
await tester.PayTester.GetService<NotificationSender>()
|
||||||
|
.SendNotification(new UserScope(user.UserId), new InviteAcceptedNotification{
|
||||||
|
UserId = user.UserId,
|
||||||
|
UserEmail = user.Email,
|
||||||
|
StoreId = store2.Id,
|
||||||
|
StoreName = store2.Name
|
||||||
|
});
|
||||||
|
notifications = (await client.GetNotifications(storeId: [store2.Id])).ToList();
|
||||||
|
Assert.Single(notifications);
|
||||||
|
|
||||||
|
notification = notifications.First();
|
||||||
|
Assert.Equal(store2.Id, notification.StoreId);
|
||||||
|
Assert.Equal($"User {user.Email} accepted the invite to {store2.Name}.", notification.Body);
|
||||||
|
|
||||||
|
Assert.Equal(2, (await client.GetNotifications(storeId: [store1.Id, store2.Id])).Count());
|
||||||
|
Assert.Equal(2, (await client.GetNotifications()).Count());
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
var settings = await client.GetNotificationSettings();
|
var settings = await client.GetNotificationSettings();
|
||||||
Assert.True(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -36,71 +37,58 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
_notificationHandlers = notificationHandlers;
|
_notificationHandlers = notificationHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanViewNotificationsForUser,
|
[Authorize(Policy = Policies.CanViewNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
||||||
[HttpGet("~/api/v1/users/me/notifications")]
|
[HttpGet("~/api/v1/users/me/notifications")]
|
||||||
public async Task<IActionResult> GetNotifications(bool? seen = null, [FromQuery] int? skip = null, [FromQuery] int? take = null)
|
public async Task<IActionResult> GetNotifications(bool? seen = null, [FromQuery] int? skip = null, [FromQuery] int? take = null, [FromQuery] string[]? storeId = null)
|
||||||
{
|
{
|
||||||
var items = await _notificationManager.GetNotifications(new NotificationsQuery()
|
var items = await _notificationManager.GetNotifications(new NotificationsQuery
|
||||||
{
|
{
|
||||||
Seen = seen,
|
Seen = seen,
|
||||||
UserId = _userManager.GetUserId(User),
|
UserId = _userManager.GetUserId(User),
|
||||||
Skip = skip,
|
Skip = skip,
|
||||||
Take = take
|
Take = take,
|
||||||
|
StoreIds = storeId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(items.Items.Select(ToModel));
|
return Ok(items.Items.Select(ToModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanViewNotificationsForUser,
|
[Authorize(Policy = Policies.CanViewNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
||||||
[HttpGet("~/api/v1/users/me/notifications/{id}")]
|
[HttpGet("~/api/v1/users/me/notifications/{id}")]
|
||||||
public async Task<IActionResult> GetNotification(string id)
|
public async Task<IActionResult> GetNotification(string id)
|
||||||
{
|
{
|
||||||
var items = await _notificationManager.GetNotifications(new NotificationsQuery()
|
var items = await _notificationManager.GetNotifications(new NotificationsQuery
|
||||||
{
|
{
|
||||||
Ids = new[] { id },
|
Ids = [id],
|
||||||
UserId = _userManager.GetUserId(User)
|
UserId = _userManager.GetUserId(User)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (items.Count == 0)
|
return items.Count == 0 ? NotificationNotFound() : Ok(ToModel(items.Items.First()));
|
||||||
{
|
|
||||||
return NotificationNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(ToModel(items.Items.First()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanManageNotificationsForUser,
|
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
||||||
[HttpPut("~/api/v1/users/me/notifications/{id}")]
|
[HttpPut("~/api/v1/users/me/notifications/{id}")]
|
||||||
public async Task<IActionResult> UpdateNotification(string id, UpdateNotification request)
|
public async Task<IActionResult> UpdateNotification(string id, UpdateNotification request)
|
||||||
{
|
{
|
||||||
var items = await _notificationManager.ToggleSeen(
|
var items = await _notificationManager.ToggleSeen(
|
||||||
new NotificationsQuery() { Ids = new[] { id }, UserId = _userManager.GetUserId(User) }, request.Seen);
|
new NotificationsQuery { Ids = [id], UserId = _userManager.GetUserId(User) }, request.Seen);
|
||||||
|
|
||||||
if (items.Count == 0)
|
return items.Count == 0 ? NotificationNotFound() : Ok(ToModel(items.First()));
|
||||||
{
|
|
||||||
return NotificationNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(ToModel(items.First()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanManageNotificationsForUser,
|
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
|
||||||
[HttpDelete("~/api/v1/users/me/notifications/{id}")]
|
[HttpDelete("~/api/v1/users/me/notifications/{id}")]
|
||||||
public async Task<IActionResult> DeleteNotification(string id)
|
public async Task<IActionResult> DeleteNotification(string id)
|
||||||
{
|
{
|
||||||
await _notificationManager.Remove(new NotificationsQuery()
|
await _notificationManager.Remove(new NotificationsQuery
|
||||||
{
|
{
|
||||||
Ids = new[] { id },
|
Ids = [id],
|
||||||
UserId = _userManager.GetUserId(User)
|
UserId = _userManager.GetUserId(User)
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
[HttpGet("~/api/v1/users/me/notification-settings")]
|
[HttpGet("~/api/v1/users/me/notification-settings")]
|
||||||
public async Task<IActionResult> GetNotificationSettings()
|
public async Task<IActionResult> GetNotificationSettings()
|
||||||
@@ -132,7 +120,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
return Ok(model);
|
return Ok(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationData ToModel(NotificationViewModel entity)
|
private static NotificationData ToModel(NotificationViewModel entity)
|
||||||
{
|
{
|
||||||
return new NotificationData
|
return new NotificationData
|
||||||
{
|
{
|
||||||
@@ -141,10 +129,12 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Type = entity.Type,
|
Type = entity.Type,
|
||||||
CreatedTime = entity.Created,
|
CreatedTime = entity.Created,
|
||||||
Body = entity.Body,
|
Body = entity.Body,
|
||||||
|
StoreId = entity.StoreId,
|
||||||
Seen = entity.Seen,
|
Seen = entity.Seen,
|
||||||
Link = string.IsNullOrEmpty(entity.ActionLink) ? null : new Uri(entity.ActionLink)
|
Link = string.IsNullOrEmpty(entity.ActionLink) ? null : new Uri(entity.ActionLink)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private IActionResult NotificationNotFound()
|
private IActionResult NotificationNotFound()
|
||||||
{
|
{
|
||||||
return this.CreateAPIError(404, "notification-not-found", "The notification was not found");
|
return this.CreateAPIError(404, "notification-not-found", "The notification was not found");
|
||||||
|
|||||||
@@ -656,10 +656,10 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
||||||
int? skip = null, int? take = null, CancellationToken token = default)
|
int? skip = null, int? take = null, string[] storeId = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return GetFromActionResult<IEnumerable<NotificationData>>(
|
return GetFromActionResult<IEnumerable<NotificationData>>(
|
||||||
await GetController<GreenfieldNotificationsController>().GetNotifications(seen, skip, take));
|
await GetController<GreenfieldNotificationsController>().GetNotifications(seen, skip, take, storeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<NotificationData> GetNotification(string notificationId,
|
public override async Task<NotificationData> GetNotification(string notificationId,
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace BTCPayServer.Controllers
|
|||||||
[Route("notifications/{action:lowercase=Index}")]
|
[Route("notifications/{action:lowercase=Index}")]
|
||||||
public class UINotificationsController : Controller
|
public class UINotificationsController : Controller
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContextFactory _factory;
|
|
||||||
private readonly StoreRepository _storeRepo;
|
private readonly StoreRepository _storeRepo;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly NotificationManager _notificationManager;
|
private readonly NotificationManager _notificationManager;
|
||||||
@@ -27,13 +26,11 @@ namespace BTCPayServer.Controllers
|
|||||||
public UINotificationsController(
|
public UINotificationsController(
|
||||||
StoreRepository storeRepo,
|
StoreRepository storeRepo,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
NotificationManager notificationManager,
|
NotificationManager notificationManager)
|
||||||
ApplicationDbContextFactory factory)
|
|
||||||
{
|
{
|
||||||
_storeRepo = storeRepo;
|
_storeRepo = storeRepo;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_notificationManager = notificationManager;
|
_notificationManager = notificationManager;
|
||||||
_factory = factory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -49,9 +46,6 @@ namespace BTCPayServer.Controllers
|
|||||||
var stores = await _storeRepo.GetStoresByUserId(userId);
|
var stores = await _storeRepo.GetStoresByUserId(userId);
|
||||||
model.Stores = stores.Where(store => !store.Archived).OrderBy(s => s.StoreName).ToList();
|
model.Stores = stores.Where(store => !store.Archived).OrderBy(s => s.StoreName).ToList();
|
||||||
|
|
||||||
|
|
||||||
await using var dbContext = _factory.CreateContext();
|
|
||||||
|
|
||||||
var searchTerm = string.IsNullOrEmpty(model.SearchText) ? model.SearchTerm : $"{model.SearchText},{model.SearchTerm}";
|
var searchTerm = string.IsNullOrEmpty(model.SearchText) ? model.SearchTerm : $"{model.SearchText},{model.SearchTerm}";
|
||||||
var fs = new SearchString(searchTerm, timezoneOffset);
|
var fs = new SearchString(searchTerm, timezoneOffset);
|
||||||
model.Search = fs;
|
model.Search = fs;
|
||||||
@@ -63,7 +57,7 @@ namespace BTCPayServer.Controllers
|
|||||||
UserId = userId,
|
UserId = userId,
|
||||||
SearchText = model.SearchText,
|
SearchText = model.SearchText,
|
||||||
Type = fs.GetFilterArray("type"),
|
Type = fs.GetFilterArray("type"),
|
||||||
Stores = fs.GetFilterArray("store"),
|
StoreIds = fs.GetFilterArray("storeid"),
|
||||||
Seen = model.Status == "Unread" ? false : null
|
Seen = model.Status == "Unread" ? false : null
|
||||||
});
|
});
|
||||||
model.Items = res.Items;
|
model.Items = res.Items;
|
||||||
@@ -71,14 +65,13 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
|
||||||
public async Task<IActionResult> FlipRead(string id)
|
public async Task<IActionResult> FlipRead(string id)
|
||||||
{
|
{
|
||||||
if (ValidUserClaim(out var userId))
|
if (ValidUserClaim(out var userId))
|
||||||
{
|
{
|
||||||
await _notificationManager.ToggleSeen(new NotificationsQuery() { Ids = new[] { id }, UserId = userId }, null);
|
await _notificationManager.ToggleSeen(new NotificationsQuery { Ids = [id], UserId = userId }, null);
|
||||||
return RedirectToAction(nameof(Index));
|
return RedirectToAction(nameof(Index));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +84,9 @@ namespace BTCPayServer.Controllers
|
|||||||
if (ValidUserClaim(out var userId))
|
if (ValidUserClaim(out var userId))
|
||||||
{
|
{
|
||||||
var items = await
|
var items = await
|
||||||
_notificationManager.ToggleSeen(new NotificationsQuery()
|
_notificationManager.ToggleSeen(new NotificationsQuery
|
||||||
{
|
{
|
||||||
Ids = new[] { id },
|
Ids = [id],
|
||||||
UserId = userId
|
UserId = userId
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
@@ -168,7 +161,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
await _notificationManager.ToggleSeen(new NotificationsQuery() { Seen = false, UserId = userId }, true);
|
await _notificationManager.ToggleSeen(new NotificationsQuery { Seen = false, UserId = userId }, true);
|
||||||
return LocalRedirect(returnUrl);
|
return LocalRedirect(returnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace BTCPayServer.Services.Notifications.Blobs
|
|||||||
}
|
}
|
||||||
vm.Identifier = notification.Identifier;
|
vm.Identifier = notification.Identifier;
|
||||||
vm.Type = notification.NotificationType;
|
vm.Type = notification.NotificationType;
|
||||||
vm.StoreId = notification?.StoreId;
|
vm.StoreId = notification.StoreId;
|
||||||
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(UIInvoiceController.Invoice),
|
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(UIInvoiceController.Invoice),
|
||||||
"UIInvoice",
|
"UIInvoice",
|
||||||
new { invoiceId = notification.InvoiceId }, _options.RootPath);
|
new { invoiceId = notification.InvoiceId }, _options.RootPath);
|
||||||
|
|||||||
@@ -151,9 +151,9 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query.Stores?.Length > 0)
|
if (query.StoreIds?.Length > 0)
|
||||||
{
|
{
|
||||||
notifications = notifications.Where(n => !string.IsNullOrEmpty(n.StoreId) && query.Stores.Contains(n.StoreId, StringComparer.OrdinalIgnoreCase)).ToList();
|
notifications = notifications.Where(n => !string.IsNullOrEmpty(n.StoreId) && query.StoreIds.Contains(n.StoreId, StringComparer.OrdinalIgnoreCase)).ToList();
|
||||||
}
|
}
|
||||||
return notifications;
|
return notifications;
|
||||||
}
|
}
|
||||||
@@ -221,6 +221,6 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
public bool? Seen { get; set; }
|
public bool? Seen { get; set; }
|
||||||
public string SearchText { get; set; }
|
public string SearchText { get; set; }
|
||||||
public string[] Type { get; set; }
|
public string[] Type { get; set; }
|
||||||
public string[] Stores { get; set; }
|
public string[] StoreIds { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
ViewData["Title"] = "Notifications";
|
ViewData["Title"] = "Notifications";
|
||||||
string status = ViewBag.Status;
|
string status = ViewBag.Status;
|
||||||
var statusFilterCount = CountArrayFilter("type");
|
var statusFilterCount = CountArrayFilter("type");
|
||||||
var storesFilterCount = CountArrayFilter("store");
|
var storesFilterCount = CountArrayFilter("storeid");
|
||||||
}
|
}
|
||||||
|
|
||||||
@functions
|
@functions
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<button id="StoresOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret w-100 w-md-auto" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="StoresOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret w-100 w-md-auto" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
@if (storesFilterCount > 0)
|
@if (storesFilterCount > 0)
|
||||||
{
|
{
|
||||||
<span>@storesFilterCount Store</span>
|
<span>@storesFilterCount Store@(storesFilterCount > 1 ? "s" : "")</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -99,8 +99,8 @@
|
|||||||
<a asp-action="Index"
|
<a asp-action="Index"
|
||||||
asp-route-count="@Model.Count"
|
asp-route-count="@Model.Count"
|
||||||
asp-route-status="@Model.Status"
|
asp-route-status="@Model.Status"
|
||||||
asp-route-searchTerm="@Model.Search.Toggle("store", store.Id)"
|
asp-route-searchTerm="@Model.Search.Toggle("storeid", store.Id)"
|
||||||
class="dropdown-item @(HasArrayFilter("store", store.Id) ? "custom-active" : "")">
|
class="dropdown-item @(HasArrayFilter("storeid", store.Id) ? "custom-active" : "")">
|
||||||
@store.StoreName
|
@store.StoreName
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,19 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "storeId",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"description": "Array of store ids to fetch the notifications for",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": "&storeId=ABCDE&storeId=FGHIJ"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "View current user's notifications",
|
"description": "View current user's notifications",
|
||||||
@@ -313,6 +326,11 @@
|
|||||||
"format": "html",
|
"format": "html",
|
||||||
"description": "The html body of the notifications"
|
"description": "The html body of the notifications"
|
||||||
},
|
},
|
||||||
|
"storeId": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true,
|
||||||
|
"description": "If related to a store, the store id of the notification"
|
||||||
|
},
|
||||||
"link": {
|
"link": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri",
|
"format": "uri",
|
||||||
|
|||||||
Reference in New Issue
Block a user