diff --git a/src/WebApp/Components/App.razor b/src/WebApp/Components/App.razor
index 4455b92b9..ec5aad921 100644
--- a/src/WebApp/Components/App.razor
+++ b/src/WebApp/Components/App.razor
@@ -14,6 +14,8 @@
+
+
diff --git a/src/WebApp/Components/Pages/Item/ItemPage.razor b/src/WebApp/Components/Pages/Item/ItemPage.razor
index f61a11b94..8b38c7da3 100644
--- a/src/WebApp/Components/Pages/Item/ItemPage.razor
+++ b/src/WebApp/Components/Pages/Item/ItemPage.razor
@@ -3,6 +3,7 @@
@inject CatalogService CatalogService
@inject BasketState BasketState
@inject NavigationManager Nav
+@inject IJSRuntime JS
@inject IProductImageUrlProvider ProductImages
@if (item is not null)
@@ -99,6 +100,7 @@ else if (notFound)
{
await BasketState.AddAsync(item);
await UpdateNumInCartAsync();
+ await TriggerConfettiAsync();
}
}
@@ -107,4 +109,16 @@ else if (notFound)
var items = await BasketState.GetBasketItemsAsync();
numInCart = items.FirstOrDefault(row => row.ProductId == ItemId)?.Quantity ?? 0;
}
+
+ private async Task TriggerConfettiAsync()
+ {
+ try
+ {
+ await JS.InvokeVoidAsync("eshopConfetti.launch");
+ }
+ catch (JSDisconnectedException)
+ {
+ // If the connection has been interrupted, there's nowhere to display confetti.
+ }
+ }
}
diff --git a/src/WebApp/wwwroot/js/confetti.js b/src/WebApp/wwwroot/js/confetti.js
new file mode 100644
index 000000000..fc3f319e9
--- /dev/null
+++ b/src/WebApp/wwwroot/js/confetti.js
@@ -0,0 +1,39 @@
+window.eshopConfetti = window.eshopConfetti || {
+ launch: () => {
+ if (typeof confetti !== "function") {
+ return;
+ }
+
+ const duration = 2000;
+ const animationEnd = Date.now() + duration;
+ const defaults = {
+ spread: 360,
+ ticks: 180,
+ gravity: 0.8,
+ decay: 0.94,
+ startVelocity: 60,
+ zIndex: 1000,
+ scalar: 1.2,
+ };
+
+ const randomInRange = (min, max) => Math.random() * (max - min) + min;
+
+ const shoot = () => {
+ confetti({
+ ...defaults,
+ particleCount: 180,
+ origin: { x: randomInRange(0.1, 0.9), y: randomInRange(0.1, 0.3) },
+ colors: ["#ff577f", "#ff884b", "#ffd384", "#fff9b0", "#62cdff", "#8c52ff"],
+ });
+ };
+
+ const frame = () => {
+ shoot();
+ if (Date.now() < animationEnd) {
+ requestAnimationFrame(frame);
+ }
+ };
+
+ frame();
+ },
+};