mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-21 23:34:25 +01:00
problem: can't view merit requests
This commit is contained in:
@@ -1,35 +1,65 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import { base } from '$app/paths';
|
|
||||||
import { Button, buttonVariants } from '$lib/components/ui/button/index.js';
|
import { Button, buttonVariants } from '$lib/components/ui/button/index.js';
|
||||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
import * as Alert from '@/components/ui/alert';
|
import * as Alert from '@/components/ui/alert';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
||||||
import { getRocketURL } from '@/helpers';
|
|
||||||
import { ndk } from '@/ndk';
|
import { ndk } from '@/ndk';
|
||||||
import { currentUser } from '@/stores/session';
|
import { currentUser } from '@/stores/session';
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import type NDKSvelte from '@nostr-dev-kit/ndk-svelte';
|
import type NDKSvelte from '@nostr-dev-kit/ndk-svelte';
|
||||||
import { Terminal } from 'lucide-svelte';
|
import { Terminal } from 'lucide-svelte';
|
||||||
import Todo from './Todo.svelte';
|
import Todo from './Todo.svelte';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
|
|
||||||
export let rocketEvent: NDKEvent;
|
export let rocketEvent: NDKEvent;
|
||||||
|
|
||||||
let problem: string;
|
let problem: string = '';
|
||||||
let solution: string;
|
let solution: string = '';
|
||||||
let image: string;
|
|
||||||
let merits: number = 0;
|
let merits: number = 0;
|
||||||
let sats: number = 0;
|
let sats: string = '';
|
||||||
let wts = false;
|
let wts = false;
|
||||||
let minimum:number = 0;
|
let minimum: string = '';
|
||||||
|
let _minimum_from_sats = false;
|
||||||
|
|
||||||
|
let last_sale_price = 1 / 1;
|
||||||
|
|
||||||
|
let num = /^\d+$/;
|
||||||
|
|
||||||
|
let open = false;
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (wts && minimum == 0) {
|
if (!num.test(sats)) {
|
||||||
minimum = sats
|
sats = '';
|
||||||
}
|
}
|
||||||
|
if (!num.test(minimum)) {
|
||||||
|
minimum = '';
|
||||||
|
}
|
||||||
|
merits = parseInt(sats, 10) * last_sale_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (wts && minimum == '' && !_minimum_from_sats) {
|
||||||
|
_minimum_from_sats = true;
|
||||||
|
minimum = sats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidUrl(string:string):boolean {
|
||||||
|
try {
|
||||||
|
new URL(string);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSolution(solution:string) {
|
||||||
|
if (solution.length > 0) {
|
||||||
|
return isValidUrl(solution)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function publish(ndk: NDKSvelte) {
|
function publish(ndk: NDKSvelte) {
|
||||||
@@ -45,23 +75,26 @@
|
|||||||
e.kind = 1409;
|
e.kind = 1409;
|
||||||
e.created_at = Math.floor(new Date().getTime() / 1000);
|
e.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
e.tags.push(['problem', 'text', problem]);
|
e.tags.push(['problem', 'text', problem]);
|
||||||
|
if (solution.length > 0) {
|
||||||
e.tags.push(['solution', 'url', solution]);
|
e.tags.push(['solution', 'url', solution]);
|
||||||
|
}
|
||||||
e.tags.push(['a', `31108:${rocketEvent.pubkey}:${rocketEvent.dTag}`]);
|
e.tags.push(['a', `31108:${rocketEvent.pubkey}:${rocketEvent.dTag}`]);
|
||||||
e.tags.push(['merits', sats.toString(10)]);
|
e.tags.push(['merits', merits.toString(10)]);
|
||||||
e.tags.push(['sats', sats.toString(10)]);
|
e.tags.push(['sats', sats]);
|
||||||
console.log(e.rawEvent());
|
console.log(e.rawEvent());
|
||||||
e.publish().then((x) => {
|
e.publish().then((x) => {
|
||||||
console.log(x);
|
console.log(x);
|
||||||
goto(`${base}/rockets/${getRocketURL(e)}`);
|
open = false
|
||||||
|
//goto(`${base}/rockets/${getRocketURL(e)}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Root>
|
<Dialog.Root bind:open>
|
||||||
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}
|
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}
|
||||||
>Create a Merit Request</Dialog.Trigger
|
>Create a Merit Request</Dialog.Trigger
|
||||||
>
|
>
|
||||||
<Dialog.Content class="sm:max-w-[425px]">
|
<Dialog.Content class="sm:max-w-[625px]">
|
||||||
{#if !currentUser}
|
{#if !currentUser}
|
||||||
<Alert.Root>
|
<Alert.Root>
|
||||||
<Terminal class="h-4 w-4" />
|
<Terminal class="h-4 w-4" />
|
||||||
@@ -80,7 +113,7 @@
|
|||||||
bind:value={problem}
|
bind:value={problem}
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="Describe the problem you solved, links to github are also acceptable"
|
placeholder="Describe the problem you solved, links to github are also acceptable"
|
||||||
class="col-span-3"
|
class="col-span-3 {problem.length < 10 ? 'border-red-600' : 'border-green-700'}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
@@ -89,20 +122,14 @@
|
|||||||
bind:value={solution}
|
bind:value={solution}
|
||||||
id="desc"
|
id="desc"
|
||||||
placeholder="Link to your solution (e.g. a merged PR or some other evidence)"
|
placeholder="Link to your solution (e.g. a merged PR or some other evidence)"
|
||||||
class="col-span-3"
|
class="col-span-3 {validateSolution(solution)? 'border-green-700':'border-red-600'}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="sats" class="text-right">Sats</Label>
|
<Label for="sats" class="text-right">Value of your work (Sats)</Label>
|
||||||
<Input
|
<Input bind:value={sats} id="price" placeholder="Sats" class="col-span-1 {parseInt(sats, 10) > 0?'border-green-700':'border-red-600'}" />
|
||||||
bind:value={sats}
|
{#if parseInt(sats, 10) > 0}<Label class="text-left">({merits.toString()} Merits)</Label>
|
||||||
id="price"
|
{/if}
|
||||||
placeholder="Number of Merits you are requesting"
|
|
||||||
class="col-span-3"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="grid items-center gap-4">
|
|
||||||
<span>You are requesting {sats} Merits</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
@@ -117,20 +144,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if wts}
|
{#if wts}
|
||||||
If your Merit Request is approved, it will be auctioned to potential sponsors. You can set a minimum amount below which your Approved Merit Request will not be sold.
|
Your Merits will be auctioned to potential sponsors as soon as it is approved, enabling you
|
||||||
|
to be paid in Sats for your work. Tip: you don't have to decide right now, you can do this
|
||||||
|
at any time.
|
||||||
|
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="sats" class="text-right col-span-2">Minimum Price (Sats)</Label>
|
<Label for="sats" class="col-span-2 text-right">Auction Reserve Price (Sats)</Label>
|
||||||
<Input
|
<Input bind:value={minimum} id="price" placeholder="Reserve Price" class="col-span-1" />
|
||||||
bind:value={minimum}
|
|
||||||
id="price"
|
|
||||||
placeholder="Number of Merits you are requesting"
|
|
||||||
class="col-span-2"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Footer>
|
<Dialog.Footer>
|
||||||
<Button
|
<Button
|
||||||
@@ -139,10 +161,9 @@
|
|||||||
}}
|
}}
|
||||||
type="submit">Publish</Button
|
type="submit">Publish</Button
|
||||||
>
|
>
|
||||||
|
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
<Todo
|
<Todo
|
||||||
text={['validate sane field entries', 'Sats must be number', 'highlight errors somehow']}
|
text={['remove white border on focus so that the validation indication color can be seen']}
|
||||||
/>
|
/>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
|
|||||||
96
src/components/MeritRequests.svelte
Normal file
96
src/components/MeritRequests.svelte
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
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 { derived } from 'svelte/store';
|
||||||
|
|
||||||
|
export let rocket: NDKEvent;
|
||||||
|
|
||||||
|
let _merits = $ndk.storeSubscribe(
|
||||||
|
[{ '#a': [`31108:${rocket.author.pubkey}:${rocket.dTag}`], kinds: [1409 as NDKKind] }],
|
||||||
|
{
|
||||||
|
subId: `${rocket.dTag}_merits`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
_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)) {
|
||||||
|
map.set(meritRequest.ID, meritRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
|
//todo: update rocket event with confirmed zaps if we have votepower
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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.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="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.Row>
|
||||||
|
</Table.Header>
|
||||||
|
<Table.Body>
|
||||||
|
{#each $merits as [id, merit], _ (id)}
|
||||||
|
<Table.Row
|
||||||
|
on:click={() => {
|
||||||
|
console.log(merit.Request.rawEvent());
|
||||||
|
}}
|
||||||
|
class="bg-accent cursor-pointer"
|
||||||
|
>
|
||||||
|
<Table.Cell>
|
||||||
|
<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 md:inline-block max-w-32 truncate p-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell class="hidden md:table-cell text-left"
|
||||||
|
>{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"
|
||||||
|
>{unixToRelativeTime(merit.TimeStamp * 1000)}</Table.Cell
|
||||||
|
>
|
||||||
|
</Table.Row>
|
||||||
|
{/each}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
</Card.Content>
|
||||||
|
</Card.Root>
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||||
import { RocketProduct } from '@/event_helpers/rockets';
|
import * as Card from '@/components/ui/card';
|
||||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import { writable, type Readable, type Writable } from 'svelte/store';
|
import CreateMeritRequest from './CreateMeritRequest.svelte';
|
||||||
|
import CreateNewProduct from './CreateNewProduct.svelte';
|
||||||
import MeritsAndSatflow from './MeritsAndSatflow.svelte';
|
import MeritsAndSatflow from './MeritsAndSatflow.svelte';
|
||||||
import ProductFomo from './ProductFomo.svelte';
|
import ProductFomo from './ProductFomo.svelte';
|
||||||
import Todo from './Todo.svelte';
|
|
||||||
import ProductCard from './ProductCard.svelte';
|
|
||||||
import CreateNewProduct from './CreateNewProduct.svelte';
|
|
||||||
import type { ExtendedBaseType } from '@nostr-dev-kit/ndk-svelte';
|
|
||||||
import ProposedProducts from './ProposedProducts.svelte';
|
import ProposedProducts from './ProposedProducts.svelte';
|
||||||
import * as Card from '@/components/ui/card';
|
import Todo from './Todo.svelte';
|
||||||
import CreateMeritRequest from './CreateMeritRequest.svelte';
|
import MeritRequests from './MeritRequests.svelte';
|
||||||
|
|
||||||
export let rocket: NDKEvent;
|
export let rocket: NDKEvent;
|
||||||
</script>
|
</script>
|
||||||
@@ -41,6 +38,8 @@
|
|||||||
|
|
||||||
<ProposedProducts {rocket} />
|
<ProposedProducts {rocket} />
|
||||||
|
|
||||||
|
|
||||||
|
<MeritRequests {rocket} />
|
||||||
<Card.Root class="sm:col-span-3">
|
<Card.Root class="sm:col-span-3">
|
||||||
<Card.Header class="pb-3">
|
<Card.Header class="pb-3">
|
||||||
<Card.Title>Actions</Card.Title>
|
<Card.Title>Actions</Card.Title>
|
||||||
|
|||||||
40
src/lib/event_helpers/merits.ts
Normal file
40
src/lib/event_helpers/merits.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||||
|
import { getNumberFromTag } from "./rockets";
|
||||||
|
|
||||||
|
export class MeritRequest {
|
||||||
|
ID: string;
|
||||||
|
Sats: 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
|
||||||
|
}
|
||||||
|
Valid(rocket: NDKEvent): boolean {
|
||||||
|
//todo: validate pubkey is in WoT
|
||||||
|
let valid = true;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,10 +120,18 @@ function getZapRequest(zapReceipt: NDKEvent): NDKEvent | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getZapAmount(zapRequest?: NDKEvent): number {
|
function getZapAmount(zapRequest?: NDKEvent): number {
|
||||||
let amount = 0;
|
return getNumberFromTag("amount", zapRequest)
|
||||||
let amountTag = zapRequest?.getMatchingTags('amount');
|
|
||||||
if (amountTag?.length == 1) {
|
|
||||||
amount = parseInt(amountTag[0][1], 10);
|
|
||||||
}
|
}
|
||||||
return amount;
|
|
||||||
|
export function getNumberFromTag(tag:string, event?: NDKEvent): number {
|
||||||
|
let amountTag = event?.getMatchingTags(tag);
|
||||||
|
if (amountTag && amountTag[0] && amountTag[0][1]) {
|
||||||
|
try {
|
||||||
|
let amount = parseInt(amountTag[0][1], 10);
|
||||||
|
return amount
|
||||||
|
} catch {
|
||||||
|
console.log("ERROR: could not find number in tag: ", tag, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user