diff --git a/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js b/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js index d3787c7..6c5460a 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js +++ b/Nop.Plugin.Misc.AuctionPlugin/Content/Js/MgMessageHandler.js @@ -1,226 +1,243 @@ -var MessageHandler = (function () { - // Handlers for each message type - var animation = "slideDown"; - var handlers = { - announcement: function (data) { - var myObject = JSON.parse(data); - - var liveScreen = document.getElementById("auctionProductLiveScreenBox"); - if (!liveScreen) { - toastr.info(`
${myObject.message}
`, myObject.title, { - "closeButton": true, - "positionClass": "toast-bottom-left", - "newestOnTop": true, - "progressBar": true, - "preventDuplicates": false, - "onclick": null, - "showDuration": "30000", - "hideDuration": "1000", - "timeOut": "5000", - "extendedTimeOut": "1000", - "showEasing": "swing", - "hideEasing": "linear", - "showMethod": animation, - "hideMethod": "fadeOut" - }); - $('.toast-info').css("background-color", "#008080"); - } - - }, - bidNotification: function (data) { +var MessageHandler = (function() { + // Handlers for each message type + var animation = "slideDown"; + var handlers = { + announcement: function(messageWrapper) { + var announcementNotification = JSON.parse(messageWrapper.data); - - console.log(data); - var myObject = JSON.parse(data); - console.log(myObject); + var liveScreen = document.getElementById("auctionProductLiveScreenBox"); + if (!liveScreen && !messageWrapper.hideToaster) { + toastr.info(`
${announcementNotification.message}
`, + announcementNotification.title, + { + "closeButton": true, + "positionClass": "toast-bottom-left", + "newestOnTop": true, + "progressBar": true, + "preventDuplicates": false, + "onclick": null, + "showDuration": "30000", + "hideDuration": "1000", + "timeOut": "5000", + "extendedTimeOut": "1000", + "showEasing": "swing", + "hideEasing": "linear", + "showMethod": animation, + "hideMethod": "fadeOut" + }); + $('.toast-info').css("background-color", "#008080"); + } + }, - var auctionDto = myObject.auctionDto; - var productToAuctionDto = auctionDto.productToAuctionDtos[0]; + bidNotification: function(messageWrapper) { + var bidNotification = JSON.parse(messageWrapper.data); - //var productAuctionMappingId = productToAuctionDto.id; - //console.log(productAuctionMappingId); + console.log(bidNotification); - var publicProductBidBox = document.getElementById("publicProductBidBox"); - var liveScreen = document.getElementById("auctionProductLiveScreenBox"); - var publicInfo = document.getElementById("publicInfoOverlay" + productToAuctionDto.productId); + var auctionDto = bidNotification.auctionDto; + var productToAuctionDto = auctionDto.productToAuctionDtos[0]; - if (publicProductBidBox) - { - //var audio = new Audio('../Plugins/Misc.AuctionPlugin/Content/ding.mp3'); - //audio.play(); - refreshPublicBidBox(myObject); - } - if (publicInfo) { - var functionName = "refreshPublicInfo" + productToAuctionDto.productId; - window[functionName](auctionDto); - } - if (liveScreen) - { - - updateOnBid(myObject); - } - else { - toastr.success(`

${myObject.currentPrice}

${myObject.productName}

`, "New bid arrived", { - "closeButton": true, - "positionClass": "toast-bottom-left", - "newestOnTop": true, - "progressBar": true, - "preventDuplicates": false, - "onclick": null, - "showDuration": "30000", - "hideDuration": "1000", - "timeOut": "5000", - "extendedTimeOut": "1000", - "showEasing": "swing", - "hideEasing": "linear", - "showMethod": animation, - "hideMethod": "fadeOut" - }); - $('.toast-success').css("background-color", "#4caf50"); - } - }, - ProductToAuctionStatusNotification: function (data) { - console.log(data); + //var productAuctionMappingId = productToAuctionDto.id; + //console.log(productAuctionMappingId); - var myObject = JSON.parse(data); - var auctionDto = myObject.auctionDto; - var productToAuctionDto = auctionDto.productToAuctionDtos[0]; - var publicProductBidBox = document.getElementById("publicProductBidBox"); - var liveScreen = document.getElementById("auctionProductLiveScreenBox"); - var publicInfo = document.getElementById("publicInfoOverlay" + productToAuctionDto.productId); + var publicProductBidBox = document.getElementById("publicProductBidBox"); + var liveScreen = document.getElementById("auctionProductLiveScreenBox"); + var publicInfo = document.getElementById("publicInfoOverlay" + productToAuctionDto.productId); - if (!liveScreen) { - var messageTitle = ""; - var messageText = ""; - var messageColor = ""; - switch (productToAuctionDto.auctionStatus) { - case AuctionStatus.None: - messageTitle = `Item reset`; - messageText = `The bids on item with index ${productToAuctionDto.sortIndex} has been resetted`; - messageColor = "#6c757d"; - break; - case AuctionStatus.Active: - messageTitle = `Item activated`; - messageText = `The bids on item with index ${productToAuctionDto.sortIndex} has been activated`; - messageColor = "#4caf50"; - break; - case AuctionStatus.FirstWarning: - messageTitle = `First warning!`; - messageText = `Hurry up! If no more bids, this item will be closed soon!`; - messageColor = "#ffc107"; - break; - case AuctionStatus.SecondWarning: - messageTitle = `Second warning!`; - messageText = `Hurry up! If no more bids, this item will be closed soon!`; - messageColor = "#dc3545"; - break; - case AuctionStatus.Pause: - messageTitle = `Administrative message`; - messageText = `The administrator has suspended the auction, it will go on soon probably`; - messageColor = "#6c757d"; - break; - case AuctionStatus.Sold: - messageTitle = `Item sold!`; - messageText = `The item has been sold, we are transitioning to the next item!`; - messageColor = "#4caf50"; - break; - case AuctionStatus.NotSold: - messageTitle = `Item closed!`; - messageText = `The item has been closed, we are transitioning to the next item!`; - messageColor = "#6c757d"; - break; - default: - break; - } + if (publicProductBidBox) { + //var audio = new Audio('../Plugins/Misc.AuctionPlugin/Content/ding.mp3'); + //audio.play(); + refreshPublicBidBox(bidNotification); + } + if (publicInfo) { + var functionName = "refreshPublicInfo" + productToAuctionDto.productId; + window[functionName](auctionDto); + } + if (liveScreen) { + updateOnBid(bidNotification); + } else if (!messageWrapper.hideToaster) { + toastr.success( + `

${bidNotification.currentPrice}

${bidNotification.productName + }

`, + "New bid arrived", + { + "closeButton": true, + "positionClass": "toast-bottom-left", + "newestOnTop": true, + "progressBar": true, + "preventDuplicates": false, + "onclick": null, + "showDuration": "30000", + "hideDuration": "1000", + "timeOut": "5000", + "extendedTimeOut": "1000", + "showEasing": "swing", + "hideEasing": "linear", + "showMethod": animation, + "hideMethod": "fadeOut" + }); + $('.toast-success').css("background-color", "#4caf50"); + } + }, - toastr.success(`

${messageText}

`, messageTitle, { - "closeButton": true, - "positionClass": "toast-bottom-left", - "newestOnTop": true, - "progressBar": true, - "preventDuplicates": false, - "onclick": null, - "showDuration": "30000", - "hideDuration": "1000", - "timeOut": "5000", - "extendedTimeOut": "1000", - "showEasing": "swing", - "hideEasing": "linear", - "showMethod": animation, - "hideMethod": "fadeOut" - }); - $('.toast-success').css("background-color", messageColor); - } - if (publicProductBidBox) { - handleAuctionUpdate(myObject); - } - if (liveScreen) { - reloadOnUpdate(); - } - if (publicInfo) { - var functionName = "refreshPublicInfo" + productToAuctionDto.productId; - window[functionName](auctionDto); - } + ProductToAuctionStatusNotification: function(messageWrapper) { + var auctionStatusNotification = JSON.parse(messageWrapper.data); - // var publicProductBidBox = document.getElementById("publicProductBidBox"); - // if (publicProductBidBox) - // { - // refreshPublicBidBox(myObject); - // } - }, - //openItemMessage: function (data) { - // var myObject = JSON.parse(data); - // toastr.success(`

${myObject.nextBidPrice}

${myObject.productName}

`, "Item auction is OPENED!", { - // "closeButton": true, - // "positionClass": "toast-top-right", - // "newestOnTop": true, - // "progressBar": true, - // "preventDuplicates": false, - // "onclick": null, - // "showDuration": "30000", - // "hideDuration": "1000", - // "timeOut": "5000", - // "extendedTimeOut": "1000", - // "showEasing": "swing", - // "hideEasing": "linear", - // "showMethod": animation, - // "hideMethod": "fadeOut" - // }); - // $('.toast-success').css("background-color", "#4caf50"); + var auctionDto = auctionStatusNotification.auctionDto; + var productToAuctionDto = auctionDto.productToAuctionDtos[0]; + var publicProductBidBox = document.getElementById("publicProductBidBox"); + var liveScreen = document.getElementById("auctionProductLiveScreenBox"); + var publicInfo = document.getElementById("publicInfoOverlay" + productToAuctionDto.productId); - // var publicProductBidBox = document.getElementById("publicProductBidBox"); - // if (publicProductBidBox) { - // refreshPublicBidBox(myObject); - // } + if (!liveScreen && !messageWrapper.hideToaster) { + var messageTitle = ""; + var messageText = ""; + var messageColor = ""; - //}, + switch (productToAuctionDto.auctionStatus) { + case AuctionStatus.None: + messageTitle = `Item reset`; + messageText = `The bids on item with index ${productToAuctionDto.sortIndex} has been resetted`; + messageColor = "#6c757d"; + break; + case AuctionStatus.Active: + messageTitle = `Item activated`; + messageText = `The bids on item with index ${productToAuctionDto.sortIndex} has been activated`; + messageColor = "#4caf50"; + break; + case AuctionStatus.FirstWarning: + messageTitle = `First warning!`; + messageText = `Hurry up! If no more bids, this item will be closed soon!`; + messageColor = "#ffc107"; + break; + case AuctionStatus.SecondWarning: + messageTitle = `Second warning!`; + messageText = `Hurry up! If no more bids, this item will be closed soon!`; + messageColor = "#dc3545"; + break; + case AuctionStatus.Pause: + messageTitle = `Administrative message`; + messageText = `The administrator has suspended the auction, it will go on soon probably`; + messageColor = "#6c757d"; + break; + case AuctionStatus.Sold: + messageTitle = `Item sold!`; + messageText = `The item has been sold, we are transitioning to the next item!`; + messageColor = "#4caf50"; + break; + case AuctionStatus.NotSold: + messageTitle = `Item closed!`; + messageText = `The item has been closed, we are transitioning to the next item!`; + messageColor = "#6c757d"; + break; + default: + break; + } - // Add more handlers as needed - default: function (data) { - console.warn("Unhandled message type:", data); - } - }; + toastr.success(`

${messageText}

`, + messageTitle, + { + "closeButton": true, + "positionClass": "toast-bottom-left", + "newestOnTop": true, + "progressBar": true, + "preventDuplicates": false, + "onclick": null, + "showDuration": "30000", + "hideDuration": "1000", + "timeOut": "5000", + "extendedTimeOut": "1000", + "showEasing": "swing", + "hideEasing": "linear", + "showMethod": animation, + "hideMethod": "fadeOut" + }); + $('.toast-success').css("background-color", messageColor); + } - // Message router to route to the appropriate handler based on message type - function messageRouter(message) { - // Parse the JSON message - try { - var parsedMessage = JSON.parse(message); - var messageType = parsedMessage.messageType; - var senderId = parsedMessage.senderId; - var messageData = parsedMessage.data; - console.log("Message type:" + messageType); - console.log("Message sender:" + senderId); - console.log("Message content" + parsedMessage.data); - // Route to appropriate handler, default if no match - (handlers[messageType] || handlers.default)(messageData); - } catch (e) { - console.error("Error parsing message:", e); - } - } + if (publicProductBidBox) { + handleAuctionUpdate(auctionStatusNotification); + } - return { - handle: messageRouter - }; + if (liveScreen) { + reloadOnUpdate(); + } + + if (publicInfo) { + var functionName = "refreshPublicInfo" + productToAuctionDto.productId; + window[functionName](auctionDto); + } + + // var publicProductBidBox = document.getElementById("publicProductBidBox"); + // if (publicProductBidBox) + // { + // refreshPublicBidBox(myObject); + // } + }, + + //openItemMessage: function(messageWrapper) { + // var openItemNotification = JSON.parse(messageWrapper.data); + + // if (!messageWrapper.hideToaster) { + // toastr.success( + // `

${openItemNotification.nextBidPrice}

${openItemNotification + // .productName}

`, + // "Item auction is OPENED!", + // { + // "closeButton": true, + // "positionClass": "toast-top-right", + // "newestOnTop": true, + // "progressBar": true, + // "preventDuplicates": false, + // "onclick": null, + // "showDuration": "30000", + // "hideDuration": "1000", + // "timeOut": "5000", + // "extendedTimeOut": "1000", + // "showEasing": "swing", + // "hideEasing": "linear", + // "showMethod": animation, + // "hideMethod": "fadeOut" + // }); + // $('.toast-success').css("background-color", "#4caf50"); + // } + + // var publicProductBidBox = document.getElementById("publicProductBidBox"); + // if (publicProductBidBox) { + // refreshPublicBidBox(openItemNotification); + // } + //}, + + // Add more handlers as needed + default: function(messageWrapperJson) { + console.warn("Unhandled message type:", messageWrapperJson); + } + }; + + // Message router to route to the appropriate handler based on message type + function messageRouter(messageWrapperJson) { + // Parse the JSON message + try { + console.log(messageWrapperJson); + + var messageWrapper = JSON.parse(messageWrapperJson); + var messageType = messageWrapper.messageType; + //var senderId = messageWrapper.senderId; + + //console.log("Message type:" + messageType); + //console.log("Message sender:" + senderId); + //console.log("Message hideToaster:" + messageWrapper.hideToaster); + //console.log("Message content" + messageWrapper.data); + + // Route to appropriate handler, default if no match + (handlers[messageType] || handlers.default)(messageWrapper); + } catch (e) { + console.error("Error parsing message:", e); + } + } + + return { + handle: messageRouter + }; })(); diff --git a/Nop.Plugin.Misc.AuctionPlugin/Hubs/AuctionHub.cs b/Nop.Plugin.Misc.AuctionPlugin/Hubs/AuctionHub.cs index b0a3370..1a9695f 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Hubs/AuctionHub.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Hubs/AuctionHub.cs @@ -1,9 +1,11 @@ -using AyCode.Core.Extensions; +using System.Text.Json.Nodes; +using AyCode.Core.Extensions; using Microsoft.AspNetCore.SignalR; using Nop.Services.Logging; using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages; using Nop.Plugin.Misc.AuctionPlugin.Services; using AyCode.Utils.Extensions; +using Newtonsoft.Json; using Nop.Services.Customers; using Nop.Core; using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums; @@ -11,10 +13,10 @@ using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Customers; using Nop.Services.Catalog; +using Newtonsoft.Json.Linq; namespace Nop.Plugin.Misc.AuctionPlugin.Hubs { - public class AuctionHub(SessionService sessionService, ILogger logger, IProductService productService, AuctionService auctionService, IWorkContext workContext, ICustomerService customerService, ICategoryService categoryService) : Hub { @@ -166,7 +168,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs else { productToAuction.BidsCount = await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id); - await SetAuctionBidPrice(revertLastBid.BidPrice, productToAuction, product, revertLastBid.CustomerId); + await SetAuctionBidPriceAsync(revertLastBid.BidPrice, productToAuction, product, revertLastBid.CustomerId); } await SendAuctionBidMessageAsync(messageWrapper, productToAuction, product, customer.Id, ResponseType.ToAllClients); @@ -216,7 +218,13 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs return ResponseType.Error; } - if (!IsValidBidPrice(activeProductAuction.CurrentPrice, bidRequestMessage.BidPrice, activeProductAuction.BidsCount > 0, customer)) + if (!await IsValidProductPricesAndSyncIfNeedAsync(activeProductAuction, product, customer)) + { + await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToAllClients); + return ResponseType.Error; + } + + if (!IsValidBidPrice(activeProductAuction, bidRequestMessage.BidPrice, customer)) { await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToCaller); return ResponseType.Error; @@ -229,7 +237,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs activeProductAuction.BidsCount++; activeProductAuction.AuctionStatus = AuctionStatus.Active; - if (!await SetAuctionBidPrice(auctionBid.BidPrice, activeProductAuction, product, customer.Id)) + if (!await SetAuctionBidPriceAsync(auctionBid.BidPrice, activeProductAuction, product, customer.Id)) { await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToCaller); return ResponseType.Error; @@ -373,7 +381,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs await HandleProductToAuctionStatusChangedRequest(customer, statusChangedMessageWrapper); } - private async Task SetAuctionBidPrice(decimal bidPrice, ProductToAuctionMapping productToAuction, Product product, int lastBidCustomerId) + private async Task SetAuctionBidPriceAsync(decimal bidPrice, ProductToAuctionMapping productToAuction, Product product, int lastBidCustomerId) { try { @@ -389,18 +397,48 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs } catch (Exception ex) { - logger.Error($"AuctionHub.HandleBidRequest(); SetAuctionBidPrice() == false", ex); + logger.Error($"AuctionHub.SetAuctionBidPriceAsync();", ex); } return false; } - private bool IsValidBidPrice(decimal productToAuctionCurrentPrice, decimal bidRequestPrice, bool hasAuctionBidInDb, Customer customer = null) + private async Task IsValidProductPricesAndSyncIfNeedAsync(ProductToAuctionMapping productToAuction, Product product, Customer customer = null) { - var nextBidPrice = AuctionService.GetNextBidPrice(productToAuctionCurrentPrice, hasAuctionBidInDb); + if (product.Price == productToAuction.CurrentPrice) return true; + + if (productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold) + { + await logger.ErrorAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); (product.Price != productToAuction.CurrentPrice && (productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold)); productPrice: {product.Price}; productToAuctionCurrentPrice: {productToAuction.CurrentPrice}; auctionStatus: {productToAuction.AuctionStatus}", null, customer); + return false; + } + + await logger.ErrorAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); (product.Price != productToAuction.CurrentPrice); productPrice: {product.Price}; productToAuctionCurrentPrice: {productToAuction.CurrentPrice}", null, customer); + + var lastBidPrice = product.OldPrice; + var lastBid = await auctionService.GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id); + + if (lastBid == null) + { + productToAuction.StartingPrice = lastBidPrice; + productToAuction.BidsCount = 0; + } + else lastBidPrice = lastBid.BidPrice; + + await SetAuctionBidPriceAsync(lastBidPrice, productToAuction, product, lastBid?.CustomerId ?? 0); + + await logger.InformationAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); Synchronize the price was successful! lastBidPrice: {lastBidPrice};", null, customer); + return false; + } + + private bool IsValidBidPrice(ProductToAuctionMapping productToAuction, decimal bidRequestPrice, Customer customer = null) + { + var hasAuctionBidInDb = productToAuction.BidsCount > 0; + var nextBidPrice = AuctionService.GetNextBidPrice(productToAuction.CurrentPrice, hasAuctionBidInDb); + if (bidRequestPrice != nextBidPrice) { - logger.Warning($"AuctionHub.IsValidBidPrice(); (bidRequestPrice != nextBidPrice); productToAuctionCurrentPrice: {productToAuctionCurrentPrice}; bidRequestPrice: {bidRequestPrice}; hasAuctionBidInDb: {hasAuctionBidInDb}", null, customer); + logger.Warning($"AuctionHub.IsValidBidPrice(); (bidRequestPrice != nextBidPrice); productToAuctionCurrentPrice: {productToAuction.CurrentPrice}; bidRequestPrice: {bidRequestPrice}; hasAuctionBidInDb: {hasAuctionBidInDb}", null, customer); return false; } @@ -479,10 +517,24 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs switch (messageWrapper.ResponseType) { case ResponseType.ToCaller: + messageWrapper.HideToaster = true; await Clients.Caller.send(messageWrapper.ToJson()); + break; case ResponseType.ToAllClients: - await Clients.All.send(messageWrapper.ToJson()); + var messageWrapperJson = messageWrapper.ToJson(); + + if (messageWrapper.HideToaster) await Clients.All.send(messageWrapperJson); + else + { + await Clients.Others.send(messageWrapperJson); + + var messageWrapperJObject = JObject.Parse(messageWrapperJson); + messageWrapperJObject["hideToaster" /*nameof(MessageWrapper.HideToaster)*/] = "true"; + + await Clients.Caller.send(messageWrapperJObject.ToString(Formatting.None)); + } + break; case ResponseType.Error: break; diff --git a/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/MessageWrapper.cs b/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/MessageWrapper.cs index a99d255..bbd4cd2 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/MessageWrapper.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Hubs/Messages/MessageWrapper.cs @@ -14,6 +14,7 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages public string RequestId { get; set; } public int RequestCount { get; set; } public ResponseType ResponseType { get; set; } + public bool HideToaster { get; set; } public string Data { get; set; } } } diff --git a/Nop.Plugin.Misc.AuctionPlugin/Hubs/SignalRMessageHandler.cs b/Nop.Plugin.Misc.AuctionPlugin/Hubs/SignalRMessageHandler.cs index 92fe2d3..7699191 100644 --- a/Nop.Plugin.Misc.AuctionPlugin/Hubs/SignalRMessageHandler.cs +++ b/Nop.Plugin.Misc.AuctionPlugin/Hubs/SignalRMessageHandler.cs @@ -27,8 +27,8 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs //- ha bid-elt 1x is, kerüljön a watch-ba //- DbTransaction-t vhogy megcsinánli! - public class SignalRMessageHandler(ILogger logger, IProductService productService, AuctionService auctionService, IHubContext hubContext, IWorkContext workContext, ICustomerService customerService, ICategoryService categoryService) - { + //public class SignalRMessageHandler(ILogger logger, IProductService productService, AuctionService auctionService, IHubContext hubContext, IWorkContext workContext, ICustomerService customerService, ICategoryService categoryService) + //{ //private readonly Mutex _handleMessageMutex = new(); //public async Task HandleMessage(MessageWrapper messageWrapper, SessionItem sessionItem, string connectionId) @@ -444,4 +444,4 @@ namespace Nop.Plugin.Misc.AuctionPlugin.Hubs // } //} } -} +//}