problem: not clear when someone makes a purchase

resolve https://github.com/nostrocket/hypergolic/issues/94
This commit is contained in:
Bob
2024-08-18 14:18:41 +08:00
parent e20bcd2012
commit f2fa67a1a9
4 changed files with 124 additions and 7 deletions

View File

@@ -2,6 +2,7 @@
import { ndk } from '@/ndk.js'; import { ndk } from '@/ndk.js';
import { Chart } from 'flowbite-svelte'; import { Chart } from 'flowbite-svelte';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { Toaster } from '$lib/components/ui/sonner';
export let data: { pubkey: string; merits: number; sats: number }[]; export let data: { pubkey: string; merits: number; sats: number }[];
let pubkeys = Array.from(data, (x) => x.pubkey); let pubkeys = Array.from(data, (x) => x.pubkey);
@@ -130,4 +131,9 @@
// </Card> // </Card>
</script> </script>
<Chart options={$o} class="py-6" /> <div class="relative h-full w-full">
<Chart options={$o} class="py-6" />
<div class="absolute left-1/2 top-0 z-20 w-[356px] -translate-x-1/2 transform">
<Toaster position="top-center" id="purchase" duration={10000} />
</div>
</div>

View File

@@ -0,0 +1,48 @@
<script lang="ts">
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
import { ndk } from '@/ndk';
import { unixToRelativeTime } from '@/helpers';
import { fetchEvent } from '@/event_helpers/products';
import { Product, Rocket, type ZapPurchase } from '@/event_helpers/rockets';
export let zapPurchase: ZapPurchase;
export let rocket: Rocket;
</script>
<div class="flex flex-col gap-1">
{#if zapPurchase.ZapReceipt.content}
<div>{zapPurchase.ZapReceipt.content}</div>
{:else}
<!-- Sometimes ZapReceipt.content is empty -->
{#await fetchEvent(zapPurchase.ProductID, $ndk)}
<div>New purchase</div>
{:then product}
<div>{`Purchase of ${new Product(product).Name()} from ${rocket.Name()}.`}</div>
{/await}
{/if}
{#if zapPurchase.BuyerPubkey}
<div class="flex flex-nowrap gap-1">
<Avatar
ndk={$ndk}
pubkey={zapPurchase.BuyerPubkey}
class="h-10 w-10 flex-none rounded-full object-cover"
/>
<Name
ndk={$ndk}
pubkey={zapPurchase.BuyerPubkey}
class="hidden max-w-32 truncate p-2 md:inline-block"
/>
</div>
{/if}
{#if zapPurchase.Amount}
<div>
Amount: {(zapPurchase.Amount / 1000).toFixed(0)}
{(zapPurchase.Amount / 1000).toFixed(0) === '1' ? 'sat' : 'sats'}
</div>
{/if}
{#if zapPurchase.ZapReceipt.created_at}
<div>
{unixToRelativeTime(zapPurchase.ZapReceipt.created_at * 1000)}
</div>
{/if}
</div>

View File

@@ -2,8 +2,10 @@
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js'; import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import Button from '@/components/ui/button/button.svelte'; import Button from '@/components/ui/button/button.svelte';
import * as Card from '@/components/ui/card'; import * as Card from '@/components/ui/card';
import PurchaseToast from './PruchaseToast.svelte';
import { Rocket, ZapPurchase } from '@/event_helpers/rockets'; import { Rocket, ZapPurchase } from '@/event_helpers/rockets';
import { devmode } from '@/stores/session'; import { devmode } from '@/stores/session';
import { toast } from 'svelte-sonner';
import type { NDKEvent } from '@nostr-dev-kit/ndk'; import type { NDKEvent } from '@nostr-dev-kit/ndk';
import BitcoinAssociations from './AssociatedBitcoinAddresses.svelte'; import BitcoinAssociations from './AssociatedBitcoinAddresses.svelte';
import MeritRequests from './MeritRequests.svelte'; import MeritRequests from './MeritRequests.svelte';
@@ -11,10 +13,47 @@
import ProductFomo from './ProductFomo.svelte'; import ProductFomo from './ProductFomo.svelte';
import ProposedProducts from './ProposedProducts.svelte'; import ProposedProducts from './ProposedProducts.svelte';
import UpdateMission from './UpdateMission.svelte'; import UpdateMission from './UpdateMission.svelte';
import { onMount } from 'svelte';
export let rocket: NDKEvent; export let rocket: NDKEvent;
$: unratifiedZaps = new Map<string, ZapPurchase>(); $: unratifiedZaps = new Map<string, ZapPurchase>();
let lastCheckTime = Date.now() / 1000; // Current time in seconds
function checkNewZaps() {
const currentTime = Date.now() / 1000;
const recentZaps = Array.from(unratifiedZaps.values()).filter(
(zap) =>
zap.ZapReceipt.created_at &&
zap.ZapReceipt.created_at > lastCheckTime &&
zap.ZapReceipt.created_at <= currentTime
);
recentZaps.forEach((zapPurchase) => {
toast(PurchaseToast, {
componentProps: {
zapPurchase,
rocket: new Rocket(rocket)
}
});
});
lastCheckTime = currentTime;
}
$: {
if (unratifiedZaps.size > 0) {
checkNewZaps();
}
}
onMount(() => {
lastCheckTime = Date.now() / 1000 - 30; // 30 seconds ago
});
$: lasted = Array.from(unratifiedZaps.values()).sort((a, b) => {
if (a.ZapReceipt.created_at && b.ZapReceipt.created_at) {
return b.ZapReceipt.created_at - a.ZapReceipt.created_at;
} else return 0;
})[0];
</script> </script>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
@@ -32,6 +71,27 @@
</Breadcrumb.Root> </Breadcrumb.Root>
</header> </header>
<main class="grid w-full flex-1 grid-cols-1 items-start gap-4 sm:grid-cols-3 md:gap-2"> <main class="grid w-full flex-1 grid-cols-1 items-start gap-4 sm:grid-cols-3 md:gap-2">
{#if $devmode}
<Button
on:click={() => {
if (!lasted) {
toast('unratifiedZaps is null');
} else {
console.log(lasted);
toast(PurchaseToast, {
componentProps: {
zapPurchase: lasted,
rocket: new Rocket(rocket)
}
});
}
}}
variant="outline">Popup Last Purchase Notification</Button
>
<Button variant="outline" on:click={() => console.log(Array.from(unratifiedZaps.values()))}
>print unratifiedZaps</Button
>
{/if}
<MeritsAndSatflow {unratifiedZaps} rocket={new Rocket(rocket)} /> <MeritsAndSatflow {unratifiedZaps} rocket={new Rocket(rocket)} />
<ProductFomo bind:unratifiedZaps rocket={new Rocket(rocket)} /> <ProductFomo bind:unratifiedZaps rocket={new Rocket(rocket)} />

View File

@@ -27,16 +27,19 @@
sessionStarted = true; sessionStarted = true;
} }
onMount(()=>{getBitcoinTip();}) onMount(() => {
setInterval(function () {
getBitcoinTip(); getBitcoinTip();
}, 2* 60 * 1000); });
setInterval(
function () {
getBitcoinTip();
},
2 * 60 * 1000
);
</script> </script>
<ModeWatcher defaultMode="dark" /> <ModeWatcher defaultMode="dark" />
<SidePanelLayout> <SidePanelLayout>
<div slot="content"><slot></slot></div> <div slot="content"><slot></slot></div>
</SidePanelLayout> </SidePanelLayout>
<style></style>