mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-21 23:34: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">
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
import * as Card from '@/components/ui/card';
|
import * as Card from '@/components/ui/card';
|
||||||
import * as Table from '@/components/ui/table';
|
import * as Table from '@/components/ui/table';
|
||||||
import { MeritRequest } from '@/event_helpers/merits';
|
import { MeritRequest } from '@/event_helpers/merits';
|
||||||
import { 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, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
|
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
|
|
||||||
export let rocket: NDKEvent;
|
export let rocket: NDKEvent;
|
||||||
@@ -23,14 +24,11 @@
|
|||||||
_merits?.unsubscribe();
|
_merits?.unsubscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let merits = derived(_merits, ($merits) => {
|
let merits = derived(_merits, ($merits) => {
|
||||||
let map = new Map<string, MeritRequest>();
|
let map = new Map<string, MeritRequest>();
|
||||||
for (let z of $merits) {
|
for (let z of $merits) {
|
||||||
let meritRequest = new MeritRequest(z);
|
let meritRequest = new MeritRequest(z);
|
||||||
if (meritRequest.Valid(rocket)) {
|
if (meritRequest.BasicValidation()) {
|
||||||
map.set(meritRequest.ID, meritRequest);
|
map.set(meritRequest.ID, meritRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,19 +41,17 @@
|
|||||||
<Card.Root class="sm:col-span-3">
|
<Card.Root class="sm:col-span-3">
|
||||||
<Card.Header class="px-7">
|
<Card.Header class="px-7">
|
||||||
<Card.Title>Merit Requests</Card.Title>
|
<Card.Title>Merit Requests</Card.Title>
|
||||||
<Card.Description
|
<Card.Description>Merit Requests</Card.Description>
|
||||||
>Merit Requests</Card.Description
|
|
||||||
>
|
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Table.Root>
|
<Table.Root>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Head>Contributor</Table.Head>
|
<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">Amount (Sats)</Table.Head>
|
||||||
<Table.Head class="table-cell">Merits</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.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
@@ -63,8 +59,9 @@
|
|||||||
<Table.Row
|
<Table.Row
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
console.log(merit.Request.rawEvent());
|
console.log(merit.Request.rawEvent());
|
||||||
|
goto(`${base}/rockets/merits/${merit.ID}`);
|
||||||
}}
|
}}
|
||||||
class="bg-accent cursor-pointer"
|
class="cursor-pointer bg-accent"
|
||||||
>
|
>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div class="flex flex-nowrap">
|
<div class="flex flex-nowrap">
|
||||||
@@ -76,16 +73,14 @@
|
|||||||
<Name
|
<Name
|
||||||
ndk={$ndk}
|
ndk={$ndk}
|
||||||
pubkey={merit.Pubkey}
|
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>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell class="hidden md:table-cell text-left"
|
<Table.Cell class="hidden text-left md:table-cell">{merit.Problem()}</Table.Cell>
|
||||||
>{merit.Problem()}</Table.Cell
|
|
||||||
>
|
|
||||||
<Table.Cell class="table-cell">{merit.Sats}</Table.Cell>
|
<Table.Cell class="table-cell">{merit.Sats}</Table.Cell>
|
||||||
<Table.Cell class="table-cell">{merit.Merits}</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
|
>{unixToRelativeTime(merit.TimeStamp * 1000)}</Table.Cell
|
||||||
>
|
>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
|||||||
@@ -1,40 +1,59 @@
|
|||||||
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
import type { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
|
||||||
import { getNumberFromTag } from "./rockets";
|
import { getNumberFromTag } from './rockets';
|
||||||
|
|
||||||
export class MeritRequest {
|
export class MeritRequest {
|
||||||
ID: string;
|
ID: string;
|
||||||
Sats: number;
|
Sats: number;
|
||||||
Merits: number;
|
Merits: number;
|
||||||
Request: NDKEvent;
|
Request: NDKEvent;
|
||||||
Pubkey: string;
|
Pubkey: string;
|
||||||
TimeStamp: number;
|
TimeStamp: number;
|
||||||
Problem():string {
|
RocketTag: string | undefined; //31108:<pubkey>:<dtag>
|
||||||
let _problem = ""
|
Problem(): string {
|
||||||
//todo: handle 1971 problem tracker event tags somehow
|
let _problem = '';
|
||||||
for (let problem of this.Request.getMatchingTags("problem")) {
|
//todo: handle 1971 problem tracker event tags somehow
|
||||||
if (problem && problem.length > 2) {
|
for (let problem of this.Request.getMatchingTags('problem')) {
|
||||||
_problem = problem[2]
|
if (problem && problem.length > 2) {
|
||||||
}
|
_problem = problem[2];
|
||||||
}
|
}
|
||||||
return _problem
|
}
|
||||||
}
|
return _problem;
|
||||||
IncludedInRocketState(rocket: NDKEvent): boolean {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
Valid(rocket: NDKEvent): boolean {
|
IncludedInRocketState(rocket: NDKEvent): boolean {
|
||||||
//todo: validate pubkey is in WoT
|
return true;
|
||||||
|
}
|
||||||
|
BasicValidation(): boolean {
|
||||||
|
//todo: make a ValidateAgainstRocket and check that pubkey is in WoT
|
||||||
let valid = true;
|
let valid = true;
|
||||||
|
if (!(this.ID.length == 64 && this.Merits > 0 && this.Pubkey.length == 64 && this.TimeStamp && this.RocketTag)) {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
return valid;
|
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) {
|
constructor(request: NDKEvent) {
|
||||||
this.Request = request;
|
this.Request = request;
|
||||||
this.ID = request.id;
|
this.ID = request.id;
|
||||||
this.Pubkey = request.pubkey
|
this.Pubkey = request.pubkey;
|
||||||
if (this.Request.created_at) {
|
if (this.Request.created_at) {
|
||||||
this.TimeStamp = this.Request.created_at
|
this.TimeStamp = this.Request.created_at;
|
||||||
}
|
}
|
||||||
|
for (let tag of this.Request.getMatchingTags('a')) {
|
||||||
this.Sats = getNumberFromTag("sats", request)
|
if (tag && tag.length > 1) {
|
||||||
this.Merits = getNumberFromTag("merits", request)
|
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 ProductCard from '../../../components/ProductCard.svelte';
|
||||||
import ProductsForRocket from '../../../components/ProductsForRocket.svelte';
|
import ProductsForRocket from '../../../components/ProductsForRocket.svelte';
|
||||||
import RocketDashboard from '../../../components/RocketDashboard.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.
|
//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%.
|
//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