mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-21 15:24:25 +01:00
problem: can't view merit request detail
This commit is contained in:
36
src/components/MeritCard.svelte
Normal file
36
src/components/MeritCard.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import type { MeritRequest } from '@/event_helpers/merits';
|
||||
import { ndk } from '@/ndk';
|
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
|
||||
|
||||
export let merit:MeritRequest;
|
||||
export let rocket:NDKEvent;
|
||||
</script>
|
||||
|
||||
|
||||
<Card.Root class="m-2">
|
||||
<Card.Header>
|
||||
<Card.Title>{merit.Problem()}</Card.Title>
|
||||
<Card.Description>
|
||||
<div class="flex flex-nowrap">
|
||||
<Avatar
|
||||
ndk={$ndk}
|
||||
pubkey={merit.Pubkey}
|
||||
class="h-10 w-10 flex-none rounded-full object-cover"
|
||||
/>
|
||||
<Name
|
||||
ndk={$ndk}
|
||||
pubkey={merit.Pubkey}
|
||||
class="hidden max-w-32 truncate p-2 md:inline-block"
|
||||
/>
|
||||
</div>
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content></Card.Content>
|
||||
<Card.Footer class="flex justify-between">
|
||||
VOTE YES VOTE NO
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
||||
38
src/components/MeritRequestDashboard.svelte
Normal file
38
src/components/MeritRequestDashboard.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||
import type { MeritRequest } from '@/event_helpers/merits';
|
||||
import { getRocketURL } from '@/helpers';
|
||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import MeritCard from './MeritCard.svelte';
|
||||
|
||||
export let rocket: NDKEvent;
|
||||
export let merit: MeritRequest;
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col sm:gap-4">
|
||||
<header class="flex items-center">
|
||||
<Breadcrumb.Root class="flex">
|
||||
<Breadcrumb.List>
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href={`${base}/rockets/${getRocketURL(rocket)}`}
|
||||
>{rocket.getMatchingTags('d')[0][1]}</Breadcrumb.Link
|
||||
>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Link href={`${base}/rockets/${getRocketURL(rocket)}`}>Merit Requests</Breadcrumb.Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Separator />
|
||||
<Breadcrumb.Item>
|
||||
<Breadcrumb.Page>{merit.Problem()}</Breadcrumb.Page>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb.List>
|
||||
</Breadcrumb.Root>
|
||||
</header>
|
||||
<main
|
||||
class="grid flex-1 items-start gap-4 p-4 sm:px-6 sm:py-0 md:gap-2 lg:grid-cols-3 xl:grid-cols-3"
|
||||
>
|
||||
<MeritCard {rocket} {merit} />
|
||||
</main>
|
||||
</div>
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import * as Card from '@/components/ui/card';
|
||||
import * as Table from '@/components/ui/table';
|
||||
import { MeritRequest } from '@/event_helpers/merits';
|
||||
import { ZapPurchase, type RocketProduct } from '@/event_helpers/rockets';
|
||||
import { unixToRelativeTime } from '@/helpers';
|
||||
import { ndk } from '@/ndk';
|
||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { derived } from 'svelte/store';
|
||||
|
||||
export let rocket: NDKEvent;
|
||||
@@ -23,14 +24,11 @@
|
||||
_merits?.unsubscribe();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
let merits = derived(_merits, ($merits) => {
|
||||
let map = new Map<string, MeritRequest>();
|
||||
for (let z of $merits) {
|
||||
let meritRequest = new MeritRequest(z);
|
||||
if (meritRequest.Valid(rocket)) {
|
||||
if (meritRequest.BasicValidation()) {
|
||||
map.set(meritRequest.ID, meritRequest);
|
||||
}
|
||||
}
|
||||
@@ -43,19 +41,17 @@
|
||||
<Card.Root class="sm:col-span-3">
|
||||
<Card.Header class="px-7">
|
||||
<Card.Title>Merit Requests</Card.Title>
|
||||
<Card.Description
|
||||
>Merit Requests</Card.Description
|
||||
>
|
||||
<Card.Description>Merit Requests</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Contributor</Table.Head>
|
||||
<Table.Head class="hidden md:table-cell text-left">Problem</Table.Head>
|
||||
<Table.Head class="hidden text-left md:table-cell">Problem</Table.Head>
|
||||
<Table.Head class="table-cell">Amount (Sats)</Table.Head>
|
||||
<Table.Head class="table-cell">Merits</Table.Head>
|
||||
<Table.Head class="hidden md:table-cell text-right">When</Table.Head>
|
||||
<Table.Head class="hidden text-right md:table-cell">When</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
@@ -63,8 +59,9 @@
|
||||
<Table.Row
|
||||
on:click={() => {
|
||||
console.log(merit.Request.rawEvent());
|
||||
goto(`${base}/rockets/merits/${merit.ID}`);
|
||||
}}
|
||||
class="bg-accent cursor-pointer"
|
||||
class="cursor-pointer bg-accent"
|
||||
>
|
||||
<Table.Cell>
|
||||
<div class="flex flex-nowrap">
|
||||
@@ -76,16 +73,14 @@
|
||||
<Name
|
||||
ndk={$ndk}
|
||||
pubkey={merit.Pubkey}
|
||||
class="hidden md:inline-block max-w-32 truncate p-2"
|
||||
class="hidden max-w-32 truncate p-2 md:inline-block"
|
||||
/>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell text-left"
|
||||
>{merit.Problem()}</Table.Cell
|
||||
>
|
||||
<Table.Cell class="hidden text-left md:table-cell">{merit.Problem()}</Table.Cell>
|
||||
<Table.Cell class="table-cell">{merit.Sats}</Table.Cell>
|
||||
<Table.Cell class="table-cell">{merit.Merits}</Table.Cell>
|
||||
<Table.Cell class="hidden md:table-cell text-right"
|
||||
<Table.Cell class="hidden text-right md:table-cell"
|
||||
>{unixToRelativeTime(merit.TimeStamp * 1000)}</Table.Cell
|
||||
>
|
||||
</Table.Row>
|
||||
|
||||
@@ -1,40 +1,59 @@
|
||||
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { getNumberFromTag } from "./rockets";
|
||||
import type { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
|
||||
import { getNumberFromTag } from './rockets';
|
||||
|
||||
export class MeritRequest {
|
||||
ID: string;
|
||||
ID: string;
|
||||
Sats: number;
|
||||
Merits: number;
|
||||
Merits: number;
|
||||
Request: NDKEvent;
|
||||
Pubkey: string;
|
||||
TimeStamp: number;
|
||||
Problem():string {
|
||||
let _problem = ""
|
||||
//todo: handle 1971 problem tracker event tags somehow
|
||||
for (let problem of this.Request.getMatchingTags("problem")) {
|
||||
if (problem && problem.length > 2) {
|
||||
_problem = problem[2]
|
||||
}
|
||||
}
|
||||
return _problem
|
||||
}
|
||||
IncludedInRocketState(rocket: NDKEvent): boolean {
|
||||
return true
|
||||
Pubkey: string;
|
||||
TimeStamp: number;
|
||||
RocketTag: string | undefined; //31108:<pubkey>:<dtag>
|
||||
Problem(): string {
|
||||
let _problem = '';
|
||||
//todo: handle 1971 problem tracker event tags somehow
|
||||
for (let problem of this.Request.getMatchingTags('problem')) {
|
||||
if (problem && problem.length > 2) {
|
||||
_problem = problem[2];
|
||||
}
|
||||
}
|
||||
return _problem;
|
||||
}
|
||||
Valid(rocket: NDKEvent): boolean {
|
||||
//todo: validate pubkey is in WoT
|
||||
IncludedInRocketState(rocket: NDKEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
BasicValidation(): boolean {
|
||||
//todo: make a ValidateAgainstRocket and check that pubkey is in WoT
|
||||
let valid = true;
|
||||
if (!(this.ID.length == 64 && this.Merits > 0 && this.Pubkey.length == 64 && this.TimeStamp && this.RocketTag)) {
|
||||
valid = false
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
RocketFilter():NDKFilter {
|
||||
if (!this.BasicValidation()) {
|
||||
return {}
|
||||
}
|
||||
return { '#d': [this.RocketTag?.split(":")[2]!], authors: [this.RocketTag?.split(":")[1]!], kinds: [31108 as number] }
|
||||
}
|
||||
constructor(request: NDKEvent) {
|
||||
this.Request = request;
|
||||
this.ID = request.id;
|
||||
this.Pubkey = request.pubkey
|
||||
if (this.Request.created_at) {
|
||||
this.TimeStamp = this.Request.created_at
|
||||
}
|
||||
|
||||
this.Sats = getNumberFromTag("sats", request)
|
||||
this.Merits = getNumberFromTag("merits", request)
|
||||
this.ID = request.id;
|
||||
this.Pubkey = request.pubkey;
|
||||
if (this.Request.created_at) {
|
||||
this.TimeStamp = this.Request.created_at;
|
||||
}
|
||||
for (let tag of this.Request.getMatchingTags('a')) {
|
||||
if (tag && tag.length > 1) {
|
||||
if (tag[1].split(':') && tag[1].split(':').length == 3) {
|
||||
if ((tag[1].split(':')[0] = '31108')) {
|
||||
this.RocketTag = tag[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Sats = getNumberFromTag('sats', request);
|
||||
this.Merits = getNumberFromTag('merits', request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
import ProductCard from '../../../components/ProductCard.svelte';
|
||||
import ProductsForRocket from '../../../components/ProductsForRocket.svelte';
|
||||
import RocketDashboard from '../../../components/RocketDashboard.svelte';
|
||||
import Todo from '../../../components/Todo.svelte';
|
||||
//flow if we only have a d-tag: fetch all 31108's with this d-tag, sort by WoT, put Nostrocket Name Service one at the top. Dedupe same rocket (same state, shadows) from multiple users, just show them all as everyone agreeing.
|
||||
//second pass: fetch ignition event for each, rebuild current state and validate all proofs, compute votepower and display only the states with > 50%.
|
||||
|
||||
|
||||
82
src/routes/rockets/merits/[merit]/+page.svelte
Normal file
82
src/routes/rockets/merits/[merit]/+page.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { MeritRequest } from '@/event_helpers/merits';
|
||||
import { ndk } from '@/ndk';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import type { ExtendedBaseType, NDKEventStore } from '@nostr-dev-kit/ndk-svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { derived, type Readable } from 'svelte/store';
|
||||
import Heading from '../../../../components/Heading.svelte';
|
||||
import MeritRequestDashboard from '../../../../components/MeritRequestDashboard.svelte';
|
||||
|
||||
let meritRequestID = $page.params.merit;
|
||||
|
||||
let meritRequest:MeritRequest | undefined;
|
||||
|
||||
$: {
|
||||
if (meritRequestID.length == 64 && !meritRequest) {
|
||||
$ndk.fetchEvent(meritRequestID).then(e=>{
|
||||
if (e) {
|
||||
meritRequest = new MeritRequest(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let rocketEvents: NDKEventStore<NDKEvent> | undefined;
|
||||
let latestRocketEvent: Readable<ExtendedBaseType<NDKEvent> | undefined>;
|
||||
|
||||
onDestroy(() => {
|
||||
rocketEvents?.unsubscribe();
|
||||
});
|
||||
|
||||
$: {
|
||||
if (meritRequest && meritRequest.BasicValidation()) {
|
||||
//the user wants the latest valid state of this rocket
|
||||
rocketEvents = $ndk.storeSubscribe(
|
||||
[
|
||||
meritRequest.RocketFilter()
|
||||
],
|
||||
{ subId: meritRequest.RocketTag!.split(":")[2] }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$: {
|
||||
if (rocketEvents) {
|
||||
latestRocketEvent = derived(rocketEvents, ($events) => {
|
||||
if (rocketEvents) {
|
||||
let sorted = $events.filter((e) => {
|
||||
return e.kind == 31108;
|
||||
});
|
||||
sorted = sorted.toSorted((a, b) => {
|
||||
return a.created_at - b.created_at;
|
||||
});
|
||||
return sorted[0];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if ($latestRocketEvent) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//todo: check that this zap is not already included in the payment JSON for the product
|
||||
//todo: list purchases on the rocket page (from product tags, as well as zap receipts that aren't yet included). Deduct total products available if not 0.
|
||||
//todo: make the page flash or something and show each time someone buys the product.
|
||||
//todo: split this out so that we can consume it for the payment page too (so that we know if there are really products left or they're all sold)
|
||||
//todo: make store of all purchases (in rocket and zaps), sort by timestamp and render with profile of buyer
|
||||
|
||||
//todo: handle shadow events (fetch the shadowed event and render it instead)
|
||||
</script>
|
||||
{#if latestRocketEvent && $latestRocketEvent && meritRequest}
|
||||
<MeritRequestDashboard rocket={$latestRocketEvent} merit={meritRequest} />
|
||||
{:else}
|
||||
<Heading title="Fetching events for this Merit Request" />
|
||||
Merit Request ID: {$page.params.merit} <br />
|
||||
{/if}
|
||||
Reference in New Issue
Block a user