problem: can't use product groups

This commit is contained in:
gsovereignty
2024-08-04 16:34:37 +08:00
parent 0d95d62140
commit 65881e87a4
11 changed files with 239 additions and 124 deletions

View File

@@ -11,16 +11,17 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { Terminal } from 'lucide-svelte'; import { Terminal } from 'lucide-svelte';
import Todo from './Todo.svelte'; import Todo from './Todo.svelte';
import { Rocket } from '@/event_helpers/rockets'; import { Product, Rocket } from '@/event_helpers/rockets';
export let product: NDKEvent; export let product: Product;
export let rocket: NDKEvent; export let rocket: Rocket;
let parsedRocket: Rocket = new Rocket(rocket);
let price: number = 0; let price: number = 0;
let max: number = 0; let max: number = 0;
let o = false;
function publish() { function publish() {
if (!$ndk.signer) { if (!$ndk.signer) {
throw new Error('no ndk signer found'); throw new Error('no ndk signer found');
@@ -29,20 +30,19 @@
if (!author) { if (!author) {
throw new Error('no current user'); throw new Error('no current user');
} }
if (rocket.author.pubkey != author.pubkey) { if (rocket.Event.author.pubkey != author.pubkey) {
console.log(rocket.author, author); throw new Error(`${author.pubkey} is not the creator of this rocket`);
throw new Error('you are not the creator of this rocket');
} }
let event = parsedRocket.UpsertProduct(product.id, price, max); let event = rocket.UpsertProduct(product.ID(), price, max);
event.ndk = $ndk event.ndk = $ndk
event.publish().then((x) => { event.publish().then((x) => {
console.log(x); console.log(x);
goto(`${base}/products`); o = false
}).catch(()=>{ console.log("failed to publish", event.rawEvent())}); }).catch(()=>{ console.log("failed to publish", event.rawEvent())});
} }
</script> </script>
<Dialog.Root> <Dialog.Root bind:open={o}>
<Dialog.Trigger class={buttonVariants({ variant: 'default' })} <Dialog.Trigger class={buttonVariants({ variant: 'default' })}
>Make Available for Purchase</Dialog.Trigger >Make Available for Purchase</Dialog.Trigger
> >

View File

@@ -11,21 +11,21 @@
import { requestProvider } from 'webln'; import { requestProvider } from 'webln';
import QrCodeSvg from './QrCodeSvg.svelte'; import QrCodeSvg from './QrCodeSvg.svelte';
import CopyButton from './CopyButton.svelte'; import CopyButton from './CopyButton.svelte';
import type { RocketProduct } from '@/event_helpers/rockets'; import type { Product, Rocket, RocketProduct } from '@/event_helpers/rockets';
export let product: NDKEvent; export let product: Product;
export let rocketProduct: RocketProduct | undefined; export let rocketProduct: RocketProduct | undefined;
export let rocket: NDKEvent; export let rocket: Rocket;
let invoice: string | null; let invoice: string | null;
async function zap() { async function zap() {
if (rocketProduct) { if (rocketProduct) {
const z = new NDKZap({ ndk: $ndk, zappedEvent: rocket, zappedUser: rocket.author }); const z = new NDKZap({ ndk: $ndk, zappedEvent: rocket.Event, zappedUser: rocket.Event.author });
invoice = await z.createZapRequest( invoice = await z.createZapRequest(
rocketProduct.Price * 1000, rocketProduct.Price * 1000,
`Purchase of ${product.getMatchingTags('name')[0][1]} from ${rocket.dTag}`, `Purchase of ${product.Name()} from ${rocket.Event.dTag}`,
[['product', product.id]] [['product', product.ID()]]
); );
} }
} }
@@ -56,7 +56,7 @@
<Dialog.Content class="sm:max-w-[425px]"> <Dialog.Content class="sm:max-w-[425px]">
<Dialog.Header> <Dialog.Header>
<Dialog.Title <Dialog.Title
>Buy {product.getMatchingTags('name')[0][1]} from {rocket.dTag} now!</Dialog.Title >Buy {product.Name()} from {rocket.Name()} now!</Dialog.Title
> >
{#if !currentUser} {#if !currentUser}
<Alert.Root> <Alert.Root>
@@ -67,7 +67,7 @@
> >
</Alert.Root> </Alert.Root>
{/if} {/if}
<Dialog.Description>Pay now with Lightning</Dialog.Description> <Dialog.Description>Pay {rocketProduct.Price} sats now with Lightning</Dialog.Description>
</Dialog.Header> </Dialog.Header>
{#if invoice} {#if invoice}
<QrCodeSvg content={invoice} /> <QrCodeSvg content={invoice} />

View File

@@ -1,57 +1,25 @@
<script lang="ts"> <script lang="ts">
import * as Card from '$lib/components/ui/card/index.js'; import * as Card from '$lib/components/ui/card/index.js';
import type { NDKEvent } from '@nostr-dev-kit/ndk'; import { Product, Rocket } from '@/event_helpers/rockets';
import AddProductToRocket from './AddProductToRocket.svelte'; import AddProductToRocket from './AddProductToRocket.svelte';
import PayNow from './PayNow.svelte'; import PayNow from './PayNow.svelte';
import { Rocket } from '@/event_helpers/rockets';
export let product: NDKEvent; export let product: Product;
export let rocket: NDKEvent; export let rocket: Rocket;
//$page.url.searchParams.get("tab")
function validate(event: NDKEvent): boolean {
let test = 0;
if (
event.getMatchingTags('name') &&
event.getMatchingTags('name')[0] &&
event.getMatchingTags('name')[0][1]
) {
test++;
}
if (
event.getMatchingTags('description') &&
event.getMatchingTags('description')[0] &&
event.getMatchingTags('description')[0][1]
) {
test++;
}
if (
event.getMatchingTags('cover') &&
event.getMatchingTags('cover')[0] &&
event.getMatchingTags('cover')[0][1]
) {
test++;
}
return test == 3;
}
function includedInRocket(rocket:Rocket, product:NDKEvent): boolean {
return Boolean(rocket.Products().get(product.id))
}
</script> </script>
{#if validate(product)} {#if product.Validate()}
<Card.Root> <Card.Root>
<Card.Header> <Card.Header>
<Card.Title>{product.getMatchingTags('name')[0][1]}</Card.Title> <Card.Title>{product.Group()} {#if product.Option().length > 0}(variant: {product.Option()}){/if}</Card.Title>
<Card.Description>{product.getMatchingTags('description')[0][1]}</Card.Description> <Card.Description>{product.Description()}</Card.Description>
</Card.Header> </Card.Header>
{#if $$slots.default} {#if $$slots.default}
<Card.Content> <Card.Content>
<div class="flex flex-col items-center justify-center gap-2 md:flex-row"> <div class="flex flex-col items-center justify-center gap-2 md:flex-row">
<img <img
src={product.getMatchingTags('cover')[0][1]} src={product.CoverImage()}
alt="cover" alt="cover"
class="aspect-square w-[300px] object-cover" class="aspect-square w-[300px] object-cover"
/> />
@@ -59,17 +27,13 @@
</div> </div>
</Card.Content> </Card.Content>
{:else} {:else}
<img <img src={product.CoverImage()} alt="cover" class="aspect-square object-cover" />
src={product.getMatchingTags('cover')[0][1]}
alt="cover"
class="aspect-square object-cover"
/>
{/if} {/if}
<Card.Footer class="flex justify-center pt-2"> <Card.Footer class="flex justify-center pt-2">
{#if !includedInRocket(new Rocket(rocket), product)} {#if !rocket.Products().get(product.ID())}
<AddProductToRocket {product} {rocket} /> <AddProductToRocket {product} {rocket} />
{:else} {:else}
<PayNow {product} rocketProduct={new Rocket(rocket).Products().get(product.id)} {rocket} /> <PayNow {product} rocketProduct={rocket.Products().get(product.ID())} {rocket} />
{/if} {/if}
</Card.Footer> </Card.Footer>
</Card.Root> </Card.Root>

View File

@@ -3,30 +3,22 @@
import { ndk } from '@/ndk'; import { ndk } from '@/ndk';
import type { NDKEvent } from '@nostr-dev-kit/ndk'; import type { NDKEvent } from '@nostr-dev-kit/ndk';
import ProductCard from './ProductCard.svelte'; import ProductCard from './ProductCard.svelte';
export let productID: string; import { fetchEvent } from '@/event_helpers/products';
export let rocket: NDKEvent; import { Product, type Rocket } from '@/event_helpers/rockets';
let productEvent: NDKEvent | undefined; export let productID: string | undefined = undefined;
export let rocket: Rocket;
export let product:Product | undefined = undefined;
onMount(() => { onMount(() => {
$ndk.fetchEvent(productID).then((e) => { if (!product && productID) {
if (e) { fetchEvent(productID, $ndk).then(e => product = new Product(e))
productEvent = e; }
} else {
let _p = $ndk.storeSubscribe([{ids:[productID] }], { subId: productID });
_p.subscribe(x=>{
if (x[0]) {
productEvent = x[0]
_p.unsubscribe()
}
})
}
});
}); });
</script> </script>
{#if productEvent} {#if product}
<ProductCard {rocket} product={productEvent}> <ProductCard {rocket} {product}>
<slot /> <slot />
</ProductCard> </ProductCard>
{/if} {/if}

View File

@@ -1,13 +1,48 @@
<script lang="ts"> <script lang="ts">
import * as Card from '$lib/components/ui/card/index.js'; import * as Card from '$lib/components/ui/card/index.js';
import { Rocket } from '@/event_helpers/rockets'; import { Product, Rocket } from '@/event_helpers/rockets';
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import ProductCardFromId from './ProductCardFromID.svelte'; import ProductCardFromId from './ProductCardFromID.svelte';
import ProductPurchases from './ProductPurchases.svelte'; import ProductPurchases from './ProductPurchases.svelte';
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import { fetchEvent } from '@/event_helpers/products';
import { ndk } from '@/ndk';
import { derived, writable } from 'svelte/store';
import * as Pagination from '@/components/ui/pagination';
import { ChevronLeft, ChevronRight } from 'lucide-svelte';
export let rocket: NDKEvent; export let rocket: Rocket;
export let unratifiedZaps = 0; export let unratifiedZaps = 0;
let products = writable(new Map<string, Product>());
for (let [id, p] of rocket.Products()) {
fetchEvent(id, $ndk).then((e) => {
let _p = new Product(e);
if (_p.Validate()) {
products.update(existing => {
existing.set(id, _p)
return existing
})
}
});
}
let groups = derived(products, ($products) => {
let productGroups = new Map<string, Map<string, Product>>();
for (let [id, p] of $products) {
console.log(p.Group())
if (!productGroups.get(p.Group())) {
productGroups.set(p.Group(), new Map());
}
let existing = productGroups.get(p.Group())!;
existing.set(id, p);
}
let productGroupArray = new Map<string, Product[]>()
for (let [id, m] of productGroups) {
productGroupArray.set(id, Array.from(m, ([_, p]) => {return p}))
}
return productGroupArray
});
</script> </script>
<Card.Root class="sm:col-span-3"> <Card.Root class="sm:col-span-3">
@@ -16,13 +51,54 @@
<Card.Description></Card.Description> <Card.Description></Card.Description>
</Card.Header> </Card.Header>
<Card.Content class="grid grid-cols-1 gap-2"> <Card.Content class="grid grid-cols-1 gap-2">
{#each new Rocket(rocket).Products() as [id, product] (id)} {#each $groups as [identifier, products] (identifier)}
<Pagination.Root count={products.length} perPage={1} siblingCount={1} let:pages let:currentPage>
{#if currentPage} <ProductCardFromId {rocket} product={products[currentPage-1]}>
<ProductPurchases bind:unratifiedZaps {rocket} {products} />
</ProductCardFromId>{/if}
{#if products.length > 1}
<Pagination.Content>
<Pagination.Item>
<Pagination.PrevButton>
<ChevronLeft class="h-4 w-4" />
<span class="hidden sm:block">Previous Option</span>
</Pagination.PrevButton>
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === "ellipsis"}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.NextButton>
<span class="hidden sm:block">Next Option</span>
<ChevronRight class="h-4 w-4" />
</Pagination.NextButton>
</Pagination.Item>
</Pagination.Content>
{/if}
</Pagination.Root>
<!-- {#each map as [id, product]} {#if true}
{/if}{/each} -->
{/each}
<!-- {#each products as [id, product] (id)}
<div> <div>
<ProductCardFromId {rocket} productID={product.ID}> <ProductCardFromId {rocket} {product}>
<ProductPurchases bind:unratifiedZaps={unratifiedZaps} {rocket} {product} /> <ProductPurchases bind:unratifiedZaps {rocket} product={rocket.Products().get(id)} />
</ProductCardFromId> </ProductCardFromId>
</div> </div>
{/each} {/each} -->
</Card.Content> </Card.Content>
<Card.Footer></Card.Footer> <Card.Footer></Card.Footer>
</Card.Root> </Card.Root>

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import * as Table from '@/components/ui/table'; import * as Table from '@/components/ui/table';
import { ValidateZapPublisher, ZapPurchase, type RocketProduct } from '@/event_helpers/rockets'; import { Product, Rocket, ValidateZapPublisher, ZapPurchase, type RocketProduct } from '@/event_helpers/rockets';
import { unixToRelativeTime } from '@/helpers'; import { unixToRelativeTime } from '@/helpers';
import { ndk } from '@/ndk'; import { ndk } from '@/ndk';
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
@@ -8,15 +8,16 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { derived, writable } from 'svelte/store'; import { derived, writable } from 'svelte/store';
export let product: RocketProduct; export let products: Product[];
export let rocket: NDKEvent; //export let products: Product[];
export let rocket: Rocket;
export let unratifiedZaps:number = 0; //todo upstream bind this and parse outstanding zaps to merits and satflow component. export let unratifiedZaps:number = 0; //todo upstream bind this and parse outstanding zaps to merits and satflow component.
let zaps = $ndk.storeSubscribe( let zaps = $ndk.storeSubscribe(
[{ '#a': [`31108:${rocket.author.pubkey}:${rocket.dTag}`], kinds: [9735] }], [{ '#a': [`31108:${rocket.Event.author.pubkey}:${rocket.Event.dTag}`], kinds: [9735] }],
{ {
subId: product.ID subId: rocket.Name() + "_zaps"
} }
); );
@@ -24,21 +25,29 @@
zaps?.unsubscribe(); zaps?.unsubscribe();
}); });
let productEvent: NDKEvent | undefined; // let productEvent: NDKEvent | undefined;
onMount(() => { // onMount(() => {
$ndk.fetchEvent(product.ID).then((e) => { // $ndk.fetchEvent(product.ID).then((e) => {
if (e) { // if (e) {
productEvent = e; // productEvent = e;
} // }
}); // });
}); // });
function productsInclude(id:string) {
let included = false
for (let p of products) {
if (p.ID() == id) {included = true}
}
return included
}
let validZaps = derived(zaps, ($zaps) => { let validZaps = derived(zaps, ($zaps) => {
let zapMap = new Map<string, ZapPurchase>(); let zapMap = new Map<string, ZapPurchase>();
for (let z of $zaps) { for (let z of $zaps) {
let zapPurchase = new ZapPurchase(z); let zapPurchase = new ZapPurchase(z);
if (zapPurchase.Valid(rocket) && zapPurchase.ProductID == product.ID) { if (zapPurchase.Valid(rocket.Event) && productsInclude(zapPurchase.ProductID)) {
zapMap.set(zapPurchase.ZapReceipt.id, zapPurchase); zapMap.set(zapPurchase.ZapReceipt.id, zapPurchase);
} }
} }
@@ -48,7 +57,7 @@
let zapsNotInRocket = derived(validZaps, ($validZaps) => { let zapsNotInRocket = derived(validZaps, ($validZaps) => {
let zapMap = new Map<string, ZapPurchase>(); let zapMap = new Map<string, ZapPurchase>();
for (let [id, z] of $validZaps) { for (let [id, z] of $validZaps) {
if (!z.IncludedInRocketState(rocket)) { if (!z.IncludedInRocketState(rocket.Event)) {
zapMap.set(id, z); zapMap.set(id, z);
} }
} }
@@ -59,7 +68,7 @@
zapsNotInRocket.subscribe((z) => { zapsNotInRocket.subscribe((z) => {
z.forEach((z) => { z.forEach((z) => {
ValidateZapPublisher(rocket, z.ZapReceipt).then((result) => { ValidateZapPublisher(rocket.Event, z.ZapReceipt).then((result) => {
if (result) { if (result) {
validPubkeys.update(existing=>{ validPubkeys.update(existing=>{
existing.add(z.ZapReceipt.pubkey); existing.add(z.ZapReceipt.pubkey);

View File

@@ -1,17 +0,0 @@
<script lang="ts">
import type { NDKEvent } from '@nostr-dev-kit/ndk';
import { getMapOfProductsFromRocket } from '@/event_helpers/rockets';
import ProductCardFromID from './ProductCardFromID.svelte';
import ProductPurchases from './ProductPurchases.svelte';
export let rocketEvent: NDKEvent;
$: rocketProducts = getMapOfProductsFromRocket(rocketEvent);
</script>
{#if rocketEvent && rocketProducts.size > 0}
{#each rocketProducts as [id, product] (id)}
<ProductCardFromID rocket={rocketEvent} productID={product.ID} />
<ProductPurchases rocket={rocketEvent} {product} />{/each}
{/if}

View File

@@ -39,7 +39,7 @@
> >
<MeritsAndSatflow {unratifiedZaps} {rocket} /> <MeritsAndSatflow {unratifiedZaps} {rocket} />
<ProductFomo bind:unratifiedZaps {rocket} /> <ProductFomo bind:unratifiedZaps rocket={new Rocket(rocket)} />
<ProposedProducts {rocket} /> <ProposedProducts {rocket} />

View File

@@ -0,0 +1,23 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import type NDKSvelte from "@nostr-dev-kit/ndk-svelte";
export async function fetchEvent(id:string, ndk:NDKSvelte):Promise<NDKEvent> {
return new Promise((resolve)=>{
ndk.fetchEvent(id).then((e) => {
if (e) {
resolve(e)
} else {
let _p = ndk.storeSubscribe([{ ids: [id] }], { subId: id, closeOnEose: true });
_p.subscribe((x) => {
if (x[0]) {
let e = x[0]
_p.unsubscribe();
resolve(e)
}
});
}
});
})
}

View File

@@ -777,3 +777,70 @@ export class BitcoinAssociation {
} }
} }
} }
export class Product {
Event: NDKEvent;
Group():string {
let s = this.Name()
//let regex = /\[\w+\]/i;
//if (regex.test(this.Name())) {
let g = this.Name().substring(this.Name().indexOf("[")+1, this.Name().lastIndexOf("]"))
if (g.length > 0) {
s = g
}
//}
return s
}
Option():string {
let result = ""
let group = this.Name().substring(this.Name().indexOf("["), this.Name().lastIndexOf("]")+1)
if (group.length > 0) {
for (let s of this.Name().trim().split(group)) {
if (s.trim().length > 0) {
result = s.trim()
}
}
}
return result
}
ID():string {
return this.Event.id
}
Name():string {
return this.Event.getMatchingTags('name')[0][1]
}
Description():string {
return this.Event.getMatchingTags('description')[0][1]
}
CoverImage():string {
return this.Event.getMatchingTags('cover')[0][1]
}
Validate(): boolean {
let test = 0;
if (
this.Event.getMatchingTags('name') &&
this.Event.getMatchingTags('name')[0] &&
this.Event.getMatchingTags('name')[0][1]
) {
test++;
}
if (
this.Event.getMatchingTags('description') &&
this.Event.getMatchingTags('description')[0] &&
this.Event.getMatchingTags('description')[0][1]
) {
test++;
}
if (
this.Event.getMatchingTags('cover') &&
this.Event.getMatchingTags('cover')[0] &&
this.Event.getMatchingTags('cover')[0][1]
) {
test++;
}
return test == 3;
}
constructor(event: NDKEvent) {
this.Event = event
}
}

View File

@@ -6,6 +6,7 @@
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
import Heading from '../../components/Heading.svelte'; import Heading from '../../components/Heading.svelte';
import ProductCard from '../../components/ProductCard.svelte'; import ProductCard from '../../components/ProductCard.svelte';
import { Product, Rocket } from '@/event_helpers/rockets';
let rockets: NDKEventStore<NDKEvent> | undefined; let rockets: NDKEventStore<NDKEvent> | undefined;
let products: NDKEventStore<NDKEvent> | undefined; let products: NDKEventStore<NDKEvent> | undefined;
@@ -48,7 +49,7 @@
<Heading title={r.dTag} /> <Heading title={r.dTag} />
<div class="grid gap-2" style="grid-template-columns: repeat(auto-fit, 350px);"> <div class="grid gap-2" style="grid-template-columns: repeat(auto-fit, 350px);">
{#each p as product (product.id)} {#each p as product (product.id)}
<ProductCard {product} rocket={r} /> <ProductCard product={new Product(product)} rocket={new Rocket(r)} />
{/each} {/each}
</div> </div>
{/each} {/each}