From 154098b5dc863c7c53d613f1591b411dfe73dd3d Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 17 May 2023 10:17:26 +0100 Subject: [PATCH] toast --- packages/app/src/Pages/Layout.tsx | 2 + packages/app/src/Pages/ZapPool.tsx | 2 +- packages/app/src/Toaster.css | 12 ++++++ packages/app/src/Toaster.tsx | 53 +++++++++++++++++++++++++++ packages/app/src/ZapPoolController.ts | 11 ++++++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/Toaster.css create mode 100644 packages/app/src/Toaster.tsx diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index bfe5a0a1..415299a8 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -25,6 +25,7 @@ import Avatar from "Element/Avatar"; import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "Util"; import { getCurrentSubscription } from "Subscription"; +import Toaster from "Toaster"; export default function Layout() { const location = useLocation(); @@ -170,6 +171,7 @@ export default function Layout() { )} {window.localStorage.getItem("debug") && } + ); } diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index 7bc46e95..c2fe1280 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -74,7 +74,7 @@ export default function ZapPoolPage() { const sumPending = zapPool.reduce((acc, v) => acc + v.sum, 0); return ( -
+

diff --git a/packages/app/src/Toaster.css b/packages/app/src/Toaster.css new file mode 100644 index 00000000..a5ada87d --- /dev/null +++ b/packages/app/src/Toaster.css @@ -0,0 +1,12 @@ +.toaster { + position: fixed; + bottom: 0; + left: 0; + display: flex; + flex-direction: column-reverse; + z-index: 9999; +} + +.toaster > .card { + border: 1px solid var(--gray); +} diff --git a/packages/app/src/Toaster.tsx b/packages/app/src/Toaster.tsx new file mode 100644 index 00000000..4b48c887 --- /dev/null +++ b/packages/app/src/Toaster.tsx @@ -0,0 +1,53 @@ +import ExternalStore from "ExternalStore"; +import Icon from "Icons/Icon"; +import { ReactNode, useSyncExternalStore } from "react"; +import { unixNow } from "Util"; + +import "./Toaster.css"; + +interface ToastNotification { + element: ReactNode; + expire?: number; + icon?: string; +} + +class ToasterSlots extends ExternalStore> { + #stack: Array = []; + #cleanup = setInterval(() => this.#eatToast(), 1000); + + push(n: ToastNotification) { + n.expire ??= unixNow() + 3; + this.#stack.push(n); + this.notifyChange(); + } + + takeSnapshot(): ToastNotification[] { + return [...this.#stack]; + } + + #eatToast() { + const now = unixNow(); + this.#stack = this.#stack.filter(a => (a.expire ?? 0) > now); + this.notifyChange(); + } +} + +export const Toastore = new ToasterSlots(); + +export default function Toaster() { + const toast = useSyncExternalStore( + c => Toastore.hook(c), + () => Toastore.snapshot() + ); + + return ( +
+ {toast.map(a => ( +
+ + {a.element} +
+ ))} +
+ ); +} diff --git a/packages/app/src/ZapPoolController.ts b/packages/app/src/ZapPoolController.ts index d02dc997..8ee240f2 100644 --- a/packages/app/src/ZapPoolController.ts +++ b/packages/app/src/ZapPoolController.ts @@ -1,6 +1,9 @@ import { UserCache } from "Cache"; +import { getDisplayName } from "Element/ProfileImage"; import ExternalStore from "ExternalStore"; import { LNURL } from "LNURL"; +import { Toastore } from "Toaster"; +import { unixNow } from "Util"; import { LNWallet, WalletInvoiceState } from "Wallet"; export enum ZapPoolRecipientType { @@ -46,6 +49,14 @@ class ZapPool extends ExternalStore> { const result = await wallet.payInvoice(invoice.pr); if (result.state === WalletInvoiceState.Paid) { x.sum -= amtSend; + Toastore.push({ + element: `Sent ${amtSend.toLocaleString()} sats to ${getDisplayName( + profile, + x.pubkey + )} from your zap pool`, + expire: unixNow() + 10, + icon: "zap", + }); } else { throw new Error("Payment failed"); }