mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-21 15:24:25 +01:00
problem: can't view merit requests
This commit is contained in:
@@ -1,35 +1,65 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { Button, buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import * as Alert from '@/components/ui/alert';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import Textarea from '@/components/ui/textarea/textarea.svelte';
|
||||
import { getRocketURL } from '@/helpers';
|
||||
import { ndk } from '@/ndk';
|
||||
import { currentUser } from '@/stores/session';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import type NDKSvelte from '@nostr-dev-kit/ndk-svelte';
|
||||
import { Terminal } from 'lucide-svelte';
|
||||
import Todo from './Todo.svelte';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
|
||||
export let rocketEvent: NDKEvent;
|
||||
|
||||
let problem: string;
|
||||
let solution: string;
|
||||
let image: string;
|
||||
let problem: string = '';
|
||||
let solution: string = '';
|
||||
let merits: number = 0;
|
||||
let sats: number = 0;
|
||||
let sats: string = '';
|
||||
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) {
|
||||
minimum = sats
|
||||
if (!num.test(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) {
|
||||
@@ -45,23 +75,26 @@
|
||||
e.kind = 1409;
|
||||
e.created_at = Math.floor(new Date().getTime() / 1000);
|
||||
e.tags.push(['problem', 'text', problem]);
|
||||
if (solution.length > 0) {
|
||||
e.tags.push(['solution', 'url', solution]);
|
||||
}
|
||||
e.tags.push(['a', `31108:${rocketEvent.pubkey}:${rocketEvent.dTag}`]);
|
||||
e.tags.push(['merits', sats.toString(10)]);
|
||||
e.tags.push(['sats', sats.toString(10)]);
|
||||
e.tags.push(['merits', merits.toString(10)]);
|
||||
e.tags.push(['sats', sats]);
|
||||
console.log(e.rawEvent());
|
||||
e.publish().then((x) => {
|
||||
console.log(x);
|
||||
goto(`${base}/rockets/${getRocketURL(e)}`);
|
||||
open = false
|
||||
//goto(`${base}/rockets/${getRocketURL(e)}`);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root>
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}
|
||||
>Create a Merit Request</Dialog.Trigger
|
||||
>
|
||||
<Dialog.Content class="sm:max-w-[425px]">
|
||||
<Dialog.Content class="sm:max-w-[625px]">
|
||||
{#if !currentUser}
|
||||
<Alert.Root>
|
||||
<Terminal class="h-4 w-4" />
|
||||
@@ -80,7 +113,7 @@
|
||||
bind:value={problem}
|
||||
id="name"
|
||||
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 class="grid grid-cols-4 items-center gap-4">
|
||||
@@ -89,20 +122,14 @@
|
||||
bind:value={solution}
|
||||
id="desc"
|
||||
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 class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="sats" class="text-right">Sats</Label>
|
||||
<Input
|
||||
bind:value={sats}
|
||||
id="price"
|
||||
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>
|
||||
<Label for="sats" class="text-right">Value of your work (Sats)</Label>
|
||||
<Input bind:value={sats} id="price" placeholder="Sats" class="col-span-1 {parseInt(sats, 10) > 0?'border-green-700':'border-red-600'}" />
|
||||
{#if parseInt(sats, 10) > 0}<Label class="text-left">({merits.toString()} Merits)</Label>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
@@ -117,20 +144,15 @@
|
||||
</div>
|
||||
|
||||
{#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">
|
||||
<Label for="sats" class="text-right col-span-2">Minimum Price (Sats)</Label>
|
||||
<Input
|
||||
bind:value={minimum}
|
||||
id="price"
|
||||
placeholder="Number of Merits you are requesting"
|
||||
class="col-span-2"
|
||||
/>
|
||||
<Label for="sats" class="col-span-2 text-right">Auction Reserve Price (Sats)</Label>
|
||||
<Input bind:value={minimum} id="price" placeholder="Reserve Price" class="col-span-1" />
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Dialog.Footer>
|
||||
<Button
|
||||
@@ -139,10 +161,9 @@
|
||||
}}
|
||||
type="submit">Publish</Button
|
||||
>
|
||||
|
||||
</Dialog.Footer>
|
||||
<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.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">
|
||||
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 { writable, type Readable, type Writable } from 'svelte/store';
|
||||
import CreateMeritRequest from './CreateMeritRequest.svelte';
|
||||
import CreateNewProduct from './CreateNewProduct.svelte';
|
||||
import MeritsAndSatflow from './MeritsAndSatflow.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 * as Card from '@/components/ui/card';
|
||||
import CreateMeritRequest from './CreateMeritRequest.svelte';
|
||||
import Todo from './Todo.svelte';
|
||||
import MeritRequests from './MeritRequests.svelte';
|
||||
|
||||
export let rocket: NDKEvent;
|
||||
</script>
|
||||
@@ -41,6 +38,8 @@
|
||||
|
||||
<ProposedProducts {rocket} />
|
||||
|
||||
|
||||
<MeritRequests {rocket} />
|
||||
<Card.Root class="sm:col-span-3">
|
||||
<Card.Header class="pb-3">
|
||||
<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 {
|
||||
let amount = 0;
|
||||
let amountTag = zapRequest?.getMatchingTags('amount');
|
||||
if (amountTag?.length == 1) {
|
||||
amount = parseInt(amountTag[0][1], 10);
|
||||
return getNumberFromTag("amount", zapRequest)
|
||||
}
|
||||
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