mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-21 07:14:23 +01:00
problem: can't list amrs for sale
This commit is contained in:
29
package-lock.json
generated
29
package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
||||||
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
@@ -1676,6 +1677,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/base58-js": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/base58-js/-/base58-js-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-LkkAPP8Zu+c0SVNRTRVDyMfKVORThX+rCViget00xdgLRrKkClCTz1T7cIrpr69ShwV5XJuuoZvMvJ43yURwkA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bech32": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@@ -1687,6 +1701,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bitcoin-address-validation": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bitcoin-address-validation/-/bitcoin-address-validation-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-1uGCGl26Ye8JG5qcExtFLQfuib6qEZWNDo1ZlLlwp/z7ygUFby3IxolgEfgMGaC+LG9csbVASLcH8fRLv7DIOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base58-js": "^1.0.0",
|
||||||
|
"bech32": "^2.0.0",
|
||||||
|
"sha256-uint8array": "^0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bits-ui": {
|
"node_modules/bits-ui": {
|
||||||
"version": "0.21.10",
|
"version": "0.21.10",
|
||||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.10.tgz",
|
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.10.tgz",
|
||||||
@@ -4492,6 +4516,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
||||||
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sha256-uint8array": {
|
||||||
|
"version": "0.10.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/sha256-uint8array/-/sha256-uint8array-0.10.7.tgz",
|
||||||
|
"integrity": "sha512-1Q6JQU4tX9NqsDGodej6pkrUVQVNapLZnvkwIhddH/JqzBZF1fSaxSWNY6sziXBE8aEa2twtGkXUrwzGeZCMpQ=="
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
||||||
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
|
|||||||
@@ -44,15 +44,15 @@
|
|||||||
>
|
>
|
||||||
<Dialog.Content>
|
<Dialog.Content>
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
<Dialog.Title>Calculate the sats</Dialog.Title>
|
<Dialog.Title>How much should you request?</Dialog.Title>
|
||||||
<Dialog.Description
|
<Dialog.Description
|
||||||
>Calculate the sats of the request according to your hour rate.</Dialog.Description
|
>Use this form to calculate how many Sats you should claim. This information will be public and permanent.</Dialog.Description
|
||||||
>
|
>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
<Label for="hourly_rate">Hour rate</Label>
|
<Label for="hourly_rate">Your hourly rate in CuckLoserBucks</Label>
|
||||||
<Input bind:value={hourly_rate} id="hourly_rate" placeholder="? USD / hour" />
|
<Input bind:value={hourly_rate} id="hourly_rate" placeholder="USD hourly rate" />
|
||||||
<Label for="spent_minutes">Working hours</Label>
|
<Label for="spent_minutes">Time spent solving this problem (minutes)</Label>
|
||||||
<Input bind:value={spent_minutes} id="spent_minutes" placeholder="? minutes" />
|
<Input bind:value={spent_minutes} id="spent_minutes" placeholder="minutes" />
|
||||||
{#if calcSats}
|
{#if calcSats}
|
||||||
<div>Result: {calcSats.toFixed(0)} sats</div>
|
<div>Result: {calcSats.toFixed(0)} sats</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
import { Avatar } from '@nostr-dev-kit/ndk-svelte-components';
|
import { Avatar } from '@nostr-dev-kit/ndk-svelte-components';
|
||||||
import { NDKNip07Signer } from '@nostr-dev-kit/ndk';
|
import { NDKNip07Signer } from '@nostr-dev-kit/ndk';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (localStorage.getItem('signed-in')) {
|
if (localStorage.getItem('signed-in')) {
|
||||||
@@ -48,6 +50,9 @@
|
|||||||
<DropdownMenu.Content align="end">
|
<DropdownMenu.Content align="end">
|
||||||
<DropdownMenu.Label>My Account</DropdownMenu.Label>
|
<DropdownMenu.Label>My Account</DropdownMenu.Label>
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Item on:click={()=>{goto(`${base}/sellmerits`)}}>Sell Merits</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item>Buy Merits</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Item>Settings</DropdownMenu.Item>
|
<DropdownMenu.Item>Settings</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item>Support</DropdownMenu.Item>
|
<DropdownMenu.Item>Support</DropdownMenu.Item>
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { getRocketURL, parseProblem } from '@/helpers';
|
import { getRocketURL, parseProblem } from '@/helpers';
|
||||||
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import MeritSummaryCard from './MeritSummaryCard.svelte';
|
import MeritSummaryCard from './MeritSummaryCard.svelte';
|
||||||
|
import { Rocket } from '@/event_helpers/rockets';
|
||||||
|
|
||||||
export let rocket: NDKEvent;
|
export let rocket: NDKEvent;
|
||||||
export let merit: MeritRequest;
|
export let merit: MeritRequest;
|
||||||
@@ -37,6 +38,6 @@
|
|||||||
<main
|
<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"
|
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"
|
||||||
>
|
>
|
||||||
<MeritSummaryCard {rocket} {merit} />
|
<MeritSummaryCard parsedRocket={new Rocket(rocket)} {merit} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,17 +12,17 @@
|
|||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
|
|
||||||
export let rocket: NDKEvent;
|
//export let rocket: NDKEvent;
|
||||||
let parsedRocket = new Rocket(rocket);
|
export let parsedRocket:Rocket;// = new Rocket(rocket);
|
||||||
|
|
||||||
let _merits = $ndk.storeSubscribe(
|
let _merits = $ndk.storeSubscribe(
|
||||||
[{ '#a': [`31108:${rocket.author.pubkey}:${rocket.dTag}`], kinds: [1409 as NDKKind] }],
|
[{ '#a': [`31108:${parsedRocket.Event.author.pubkey}:${parsedRocket.Name()}`], kinds: [1409 as NDKKind] }],
|
||||||
{
|
{
|
||||||
subId: `${rocket.dTag}_merits`
|
subId: `${parsedRocket.Name()}_merits`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let _votes = $ndk.storeSubscribe({ '#a': [RocketATagFilter(rocket)], kinds: [1410 as NDKKind] });
|
let _votes = $ndk.storeSubscribe({ '#a': [RocketATagFilter(parsedRocket.Event)], kinds: [1410 as NDKKind] });
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
_merits?.unsubscribe();
|
_merits?.unsubscribe();
|
||||||
|
|||||||
@@ -21,15 +21,15 @@
|
|||||||
import MeritComment from './MeritComment.svelte';
|
import MeritComment from './MeritComment.svelte';
|
||||||
|
|
||||||
export let merit: MeritRequest;
|
export let merit: MeritRequest;
|
||||||
export let rocket: NDKEvent;
|
//export let rocket: NDKEvent;
|
||||||
let cuckPrice: number | undefined = undefined;
|
let cuckPrice: number | undefined = undefined;
|
||||||
|
|
||||||
let result: VoteDirection | undefined;
|
let result: VoteDirection | undefined;
|
||||||
|
|
||||||
let parsedRocket = new Rocket(rocket);
|
export let parsedRocket:Rocket;
|
||||||
|
|
||||||
let _votes = $ndk.storeSubscribe(
|
let _votes = $ndk.storeSubscribe(
|
||||||
{ '#a': [RocketATagFilter(rocket)], '#e': [merit.ID], kinds: [1410 as NDKKind] },
|
{ '#a': [RocketATagFilter(parsedRocket.Event)], '#e': [merit.ID], kinds: [1410 as NDKKind] },
|
||||||
{
|
{
|
||||||
subId: merit.ID
|
subId: merit.ID
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
updatedRocket.ndk = $ndk;
|
updatedRocket.ndk = $ndk;
|
||||||
updatedRocket.sign().then(() => {
|
updatedRocket.sign().then(() => {
|
||||||
updatedRocket.publish().then(() => {
|
updatedRocket.publish().then(() => {
|
||||||
goto(`${base}/rockets/${getRocketURL(rocket)}`);
|
goto(`${base}/rockets/${getRocketURL(parsedRocket.Event)}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
<Separator class="my-4" />
|
<Separator class="my-4" />
|
||||||
<div class="font-semibold">Votes</div>
|
<div class="font-semibold">Votes</div>
|
||||||
{#if $votes.size == 0}<Alert
|
{#if $votes.size == 0}<Alert
|
||||||
><Info />Waiting for existing <span class="italic">{new Rocket(rocket).Name()}</span> Merit
|
><Info />Waiting for existing <span class="italic">{parsedRocket.Name()}</span> Merit
|
||||||
holders to vote</Alert
|
holders to vote</Alert
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
{:else if result == 'blackball'}
|
{:else if result == 'blackball'}
|
||||||
<span class="scroll-m-20 text-lg font-semibold tracking-tight md:text-xl">REJECTED</span>
|
<span class="scroll-m-20 text-lg font-semibold tracking-tight md:text-xl">REJECTED</span>
|
||||||
{:else if !result}
|
{:else if !result}
|
||||||
<VoteOnMeritRequest {merit} rocket={new Rocket(rocket)} />
|
<VoteOnMeritRequest {merit} rocket={parsedRocket} />
|
||||||
{/if}
|
{/if}
|
||||||
</Card.Footer>
|
</Card.Footer>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
43
src/components/MeritTrades.svelte
Normal file
43
src/components/MeritTrades.svelte
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||||
|
import { Rocket } from '@/event_helpers/rockets';
|
||||||
|
import { getRocketURL } from '@/helpers';
|
||||||
|
import type { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
|
||||||
|
export let rocket: NDKEvent;
|
||||||
|
let parsedRocket = new Rocket(rocket)
|
||||||
|
|
||||||
|
|
||||||
|
</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>
|
||||||
|
Trade
|
||||||
|
</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"
|
||||||
|
>
|
||||||
|
Trade {parsedRocket.Name()} Merits
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
import ProposedProducts from './ProposedProducts.svelte';
|
import ProposedProducts from './ProposedProducts.svelte';
|
||||||
import Todo from './Todo.svelte';
|
import Todo from './Todo.svelte';
|
||||||
import UpdateMission from './UpdateMission.svelte';
|
import UpdateMission from './UpdateMission.svelte';
|
||||||
|
import { Rocket } from '@/event_helpers/rockets';
|
||||||
|
|
||||||
export let rocket: NDKEvent;
|
export let rocket: NDKEvent;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
|
|
||||||
<ProposedProducts {rocket} />
|
<ProposedProducts {rocket} />
|
||||||
|
|
||||||
<MeritRequests {rocket} />
|
<MeritRequests parsedRocket={new Rocket(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>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export class MeritRequest {
|
|||||||
}
|
}
|
||||||
IncludedInRocketState(rocket: Rocket): boolean {
|
IncludedInRocketState(rocket: Rocket): boolean {
|
||||||
let included = rocket.ApprovedMeritRequests();
|
let included = rocket.ApprovedMeritRequests();
|
||||||
|
console.log(39, rocket, included.get(this.ID))
|
||||||
return Boolean(included.get(this.ID));
|
return Boolean(included.get(this.ID));
|
||||||
}
|
}
|
||||||
BasicValidation(): boolean {
|
BasicValidation(): boolean {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
|
||||||
import { MapOfVotes, MeritRequest, Votes } from './merits';
|
import { MapOfVotes, MeritRequest, Votes } from './merits';
|
||||||
import { getAuthorizedZapper } from '@/helpers';
|
import { getAuthorizedZapper } from '@/helpers';
|
||||||
|
import validate from 'bitcoin-address-validation';
|
||||||
|
|
||||||
export class Rocket {
|
export class Rocket {
|
||||||
Event: NDKEvent;
|
Event: NDKEvent;
|
||||||
@@ -37,12 +38,12 @@ export class Rocket {
|
|||||||
return amr;
|
return amr;
|
||||||
}
|
}
|
||||||
TotalMerits(): number {
|
TotalMerits(): number {
|
||||||
let total = 0
|
let total = 0;
|
||||||
let amr = this.ApprovedMeritRequests()
|
let amr = this.ApprovedMeritRequests();
|
||||||
for (let [_, _amr] of amr) {
|
for (let [_, _amr] of amr) {
|
||||||
total += _amr.Merits
|
total += _amr.Merits;
|
||||||
}
|
}
|
||||||
return total
|
return total;
|
||||||
}
|
}
|
||||||
ValidateAMRProof(amrProof: NDKEvent): boolean {
|
ValidateAMRProof(amrProof: NDKEvent): boolean {
|
||||||
let result = false;
|
let result = false;
|
||||||
@@ -114,7 +115,19 @@ export class Rocket {
|
|||||||
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
||||||
event.created_at = Math.floor(new Date().getTime() / 1000);
|
event.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
event.tags.push(['merit', `${request.Pubkey}:${request.ID}:0:0:${request.Merits}`]);
|
event.tags.push(['merit', `${request.Pubkey}:${request.ID}:0:0:${request.Merits}`]);
|
||||||
event.tags.push(['proof_full', JSON.stringify(signedProof)]);
|
event.tags.push(['proof_full', JSON.stringify(signedProof.rawEvent())]);
|
||||||
|
updateIgnitionAndParentTag(event);
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
UpsertAMRAuction(request: AMRAuction): NDKEvent | undefined {
|
||||||
|
let event: NDKEvent | undefined = undefined;
|
||||||
|
if (request.ValidateAgainstRocket(this)) {
|
||||||
|
this.PrepareForUpdate();
|
||||||
|
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
||||||
|
event.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
|
|
||||||
|
event.tags.push(['proof_full', JSON.stringify(request.Event!.rawEvent())]);
|
||||||
updateIgnitionAndParentTag(event);
|
updateIgnitionAndParentTag(event);
|
||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
@@ -238,6 +251,7 @@ export class RocketAMR {
|
|||||||
LeadTime: number;
|
LeadTime: number;
|
||||||
LeadTimeUpdate: number;
|
LeadTimeUpdate: number;
|
||||||
Merits: number;
|
Merits: number;
|
||||||
|
Extra: {};
|
||||||
SatsOwed(): number {
|
SatsOwed(): number {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -436,3 +450,126 @@ export async function ValidateZapPublisher(rocket: NDKEvent, zap: NDKEvent): Pro
|
|||||||
// }).catch(()=>{reject(false)})
|
// }).catch(()=>{reject(false)})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AMRAuction {
|
||||||
|
AMRIDs: string[];
|
||||||
|
Owner: string|undefined;
|
||||||
|
StartPrice: number;
|
||||||
|
EndPrice: number;
|
||||||
|
RxAddress: string;
|
||||||
|
RocketD: string;
|
||||||
|
RocketP: string;
|
||||||
|
Merits: number;
|
||||||
|
Event: NDKEvent;
|
||||||
|
GenerateEvent(): NDKEvent {
|
||||||
|
let e = new NDKEvent();
|
||||||
|
e.kind = 1412;
|
||||||
|
e.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
|
for (let id of this.AMRIDs) {
|
||||||
|
e.tags.push(["request", id])
|
||||||
|
}
|
||||||
|
e.tags.push(['a', `31108:${this.RocketP}:${this.RocketD}`]);
|
||||||
|
//todo: allow user to set start and end auction price
|
||||||
|
|
||||||
|
e.tags.push(['price', this.StartPrice + ":" + this.EndPrice]);
|
||||||
|
e.tags.push(["onchain", this.RxAddress])
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
Push(amr: RocketAMR) {
|
||||||
|
if (this.Owner && amr.Pubkey != this.Owner) {
|
||||||
|
throw new Error("invalid pubkey")
|
||||||
|
}
|
||||||
|
this.Owner = amr.Pubkey
|
||||||
|
this.AMRIDs.push(amr.ID);
|
||||||
|
this.StartPrice += amr.Merits;
|
||||||
|
this.EndPrice += amr.Merits;
|
||||||
|
this.Merits += amr.Merits;
|
||||||
|
}
|
||||||
|
Pop(amr: RocketAMR) {
|
||||||
|
if (this.AMRIDs.includes(amr.ID) && amr.Pubkey == this.Owner) {
|
||||||
|
let n:string[] = []
|
||||||
|
for (let id of this.AMRIDs) {
|
||||||
|
if (id != amr.ID) {
|
||||||
|
n.push(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.AMRIDs = n;
|
||||||
|
//todo: allow user to set start/end price
|
||||||
|
this.StartPrice -= amr.Merits
|
||||||
|
this.EndPrice -= amr.Merits
|
||||||
|
this.Merits -= amr.Merits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Validate(): boolean {
|
||||||
|
let valid = true;
|
||||||
|
if (
|
||||||
|
this.Owner?.length != 64 ||
|
||||||
|
!this.StartPrice ||
|
||||||
|
!this.EndPrice ||
|
||||||
|
!validate(this.RxAddress) ||
|
||||||
|
this.RocketP.length != 64
|
||||||
|
) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
for (let id of this.AMRIDs) {
|
||||||
|
if (id.length != 64) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
ValidateAgainstRocket(rocket: Rocket): boolean {
|
||||||
|
let valid = true;
|
||||||
|
for (let id of this.AMRIDs) {
|
||||||
|
let rocketAMR = rocket.ApprovedMeritRequests().get(id);
|
||||||
|
if (!rocketAMR || (rocketAMR && rocketAMR.Pubkey != this.Owner) || rocketAMR.LeadTime > 0) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo: check if this AMR is already listed for sale
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
constructor(rocket?: NDKEvent, event?: NDKEvent, address?: string) {
|
||||||
|
this.AMRIDs = [];
|
||||||
|
this.Merits = 0
|
||||||
|
this.EndPrice = 0
|
||||||
|
this.StartPrice = 0
|
||||||
|
if (rocket && !event) {
|
||||||
|
this.RxAddress = address?address:""
|
||||||
|
this.RocketD = rocket.dTag!;
|
||||||
|
this.RocketP = rocket.author.pubkey;
|
||||||
|
}
|
||||||
|
if (event && !rocket) {
|
||||||
|
this.Event = event
|
||||||
|
for (let id of event.getMatchingTags("request")) {
|
||||||
|
if (id && id.length == 2 && id[1].length == 64) {
|
||||||
|
this.AMRIDs.push(id[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Owner = event.author.pubkey;
|
||||||
|
let price = event.tagValue('price');
|
||||||
|
if (price) {
|
||||||
|
let _start = price.split(':')[0];
|
||||||
|
let _end = price.split(':')[1];
|
||||||
|
let start = parseInt(_start, 10);
|
||||||
|
let end = parseInt(_end, 10);
|
||||||
|
this.StartPrice = start;
|
||||||
|
this.EndPrice = end;
|
||||||
|
}
|
||||||
|
let address = event.tagValue('onchain');
|
||||||
|
if (address) {
|
||||||
|
if (validate(address)) {
|
||||||
|
this.RxAddress = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _rocket = event.tagValue('a');
|
||||||
|
if (_rocket) {
|
||||||
|
if (_rocket.split(':').length == 3) {
|
||||||
|
this.RocketP = _rocket.split(':')[1];
|
||||||
|
this.RocketD = _rocket.split(':')[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
54
src/route_helpers/Rocket.svelte
Normal file
54
src/route_helpers/Rocket.svelte
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
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';
|
||||||
|
|
||||||
|
export let dTag: string; // = $page.url.searchParams.get('d');
|
||||||
|
export let pubkey: string; // = $page.url.searchParams.get('p');
|
||||||
|
|
||||||
|
|
||||||
|
let rocketEvents: NDKEventStore<NDKEvent> | undefined;
|
||||||
|
export let latestRocketEvent: Readable<ExtendedBaseType<NDKEvent> | undefined>;
|
||||||
|
|
||||||
|
let candidateProducts: Readable<ExtendedBaseType<NDKEvent>[]>;
|
||||||
|
onDestroy(() => {
|
||||||
|
rocketEvents?.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
rocketEvents = $ndk.storeSubscribe(
|
||||||
|
[
|
||||||
|
{ '#d': [dTag], authors: [pubkey], kinds: [31108 as number] },
|
||||||
|
{ '#a': [`31108:${pubkey}:${dTag}`] }
|
||||||
|
],
|
||||||
|
{ subId: dTag }
|
||||||
|
);
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (rocketEvents && !latestRocketEvent) {
|
||||||
|
latestRocketEvent = derived(rocketEvents, ($events) => {
|
||||||
|
let sorted = $events.filter((e) => {
|
||||||
|
return e.kind == 31108;
|
||||||
|
});
|
||||||
|
sorted = sorted.toSorted((a, b) => {
|
||||||
|
return a.created_at - b.created_at;
|
||||||
|
});
|
||||||
|
return sorted[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($latestRocketEvent && !candidateProducts) {
|
||||||
|
candidateProducts = derived(rocketEvents, ($events) => {
|
||||||
|
return $events.filter((e) => {
|
||||||
|
for (let p of $latestRocketEvent.getMatchingTags('product')) {
|
||||||
|
if (p[1].includes(e.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.kind == 1908;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
166
src/routes/buymerits/+page.svelte
Normal file
166
src/routes/buymerits/+page.svelte
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { currentUser } from '@/stores/session';
|
||||||
|
import Login from '../../components/Login.svelte';
|
||||||
|
import { ndk } from '@/ndk';
|
||||||
|
import { onDestroy } from 'svelte';
|
||||||
|
import { derived } from 'svelte/store';
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
import { type RocketAMR, AMRAuction, Rocket } from '@/event_helpers/rockets';
|
||||||
|
import * as Table from '@/components/ui/table';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
let rocketEvents = $ndk.storeSubscribe([{ kinds: [31108 as number] }], { subId: 'all_rockets' });
|
||||||
|
onDestroy(() => {
|
||||||
|
rocketEvents?.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
let rockets = derived(rocketEvents, ($rocketEvents) => {
|
||||||
|
let m = new Map<string, NDKEvent>();
|
||||||
|
for (let e of $rocketEvents) {
|
||||||
|
let existing = m.get(e.pubkey + e.dTag);
|
||||||
|
if (!existing) {
|
||||||
|
existing = e;
|
||||||
|
}
|
||||||
|
if (e.created_at > existing) {
|
||||||
|
existing = e;
|
||||||
|
}
|
||||||
|
m.set(e.pubkey + e.dTag, e);
|
||||||
|
}
|
||||||
|
return Array.from(m, ([_, e]) => e);
|
||||||
|
});
|
||||||
|
|
||||||
|
let myMeritRequests = derived([currentUser, rockets], ([$currentUser, $rockets]) => {
|
||||||
|
let merits = new Map<Rocket, RocketAMR[]>();
|
||||||
|
if ($currentUser) {
|
||||||
|
for (let r of $rockets) {
|
||||||
|
let parsedRocket = new Rocket(r);
|
||||||
|
let _merits: RocketAMR[] = [];
|
||||||
|
for (let [_, amr] of parsedRocket.ApprovedMeritRequests()) {
|
||||||
|
if (amr.Pubkey == $currentUser.pubkey) {
|
||||||
|
_merits.push(amr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merits.set(parsedRocket, _merits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merits;
|
||||||
|
});
|
||||||
|
|
||||||
|
let selected = new Map<string, AMRAuction>();
|
||||||
|
function toggleSelected(amr: RocketAMR, rocket: Rocket) {
|
||||||
|
if (selected.has(amr.ID)) {
|
||||||
|
selected.delete(amr.ID);
|
||||||
|
} else {
|
||||||
|
selected.set(amr.ID, new AMRAuction(amr, rocket.Event));
|
||||||
|
}
|
||||||
|
selected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTotal(list:Map<string, AMRAuction>):number {
|
||||||
|
let total = 0
|
||||||
|
for (let [_, amr] of list) {
|
||||||
|
total += amr.Merits
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllForRocket(rocket:Rocket, selected:Map<string, AMRAuction>):Map<string, AMRAuction> {
|
||||||
|
let thisRocket = new Map<string, AMRAuction>()
|
||||||
|
for (let [_, amr] of selected) {
|
||||||
|
if (amr.RocketD == rocket.Name() && amr.RocketP == rocket.Event.author.pubkey) {
|
||||||
|
thisRocket.set(amr.AMRID, amr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return thisRocket
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish(sales:Map<string, AMRAuction>, address: string, rocket:Rocket) {
|
||||||
|
if (!$ndk.signer) {
|
||||||
|
throw new Error('no ndk signer found');
|
||||||
|
}
|
||||||
|
let e = new NDKEvent($ndk);
|
||||||
|
let author = $currentUser;
|
||||||
|
if (!author) {
|
||||||
|
throw new Error('no current user');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
e.author = author;
|
||||||
|
e.kind = 1412;
|
||||||
|
e.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
|
for (let [_, amr] of sales) {
|
||||||
|
e.tags.push(["request", amr.AMRID])
|
||||||
|
}
|
||||||
|
|
||||||
|
e.tags.push(['a', `31108:${rocket.Event.pubkey}:${rocket.Event.dTag}`]);
|
||||||
|
//todo: allow user to set start and end auction price
|
||||||
|
let total = getTotal(getAllForRocket(rocket, sales)).toString()
|
||||||
|
e.tags.push(['price', total + ":" + total]);
|
||||||
|
e.tags.push(["onchain", address])
|
||||||
|
e.publish().then((x) => {
|
||||||
|
console.log(x, e);
|
||||||
|
|
||||||
|
//goto(`${base}/rockets/${getRocketURL(e)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let bitcoinAddress:string = ""
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class=" m-2 text-nowrap text-center text-xl">Trade your Merits for Sats</h1>
|
||||||
|
|
||||||
|
{#if $currentUser}
|
||||||
|
{#each $myMeritRequests as [rocket, amr]}
|
||||||
|
{#if amr.length > 0}
|
||||||
|
<h1>ROCKET: {rocket.Name()}</h1>
|
||||||
|
|
||||||
|
<Table.Root>
|
||||||
|
<Table.Header>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Head class="w-[100px]">Selected</Table.Head>
|
||||||
|
<Table.Head class="w-[10px]">AMR</Table.Head>
|
||||||
|
<Table.Head class="w-[10px]">Elegible</Table.Head>
|
||||||
|
<Table.Head>Merits</Table.Head>
|
||||||
|
<Table.Head class="text-right">Sats (approx)</Table.Head>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
<Table.Body>
|
||||||
|
{#each amr as a, id (a.ID)}
|
||||||
|
<Table.Row class={selected.has(a.ID) ? 'bg-orange-500 hover:bg-orange-500' : ''}>
|
||||||
|
<Table.Cell
|
||||||
|
><Checkbox
|
||||||
|
id={a.ID}
|
||||||
|
checked={selected.has(a.ID)}
|
||||||
|
on:click={() => {
|
||||||
|
toggleSelected(a, rocket);
|
||||||
|
}}
|
||||||
|
/></Table.Cell
|
||||||
|
>
|
||||||
|
<Table.Cell
|
||||||
|
><span
|
||||||
|
class="cursor-pointer font-medium underline"
|
||||||
|
on:click={() => {
|
||||||
|
goto(`${base}/rockets/merits/${a.ID}`);
|
||||||
|
}}>{a.ID.substring(0, 6)}</span
|
||||||
|
></Table.Cell
|
||||||
|
>
|
||||||
|
<Table.Cell>{a.LeadTime == 0}</Table.Cell>
|
||||||
|
<Table.Cell>{a.Merits}</Table.Cell>
|
||||||
|
<Table.Cell class="text-right">{a.Merits}</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
{/each}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
{#if selected.size > 0}
|
||||||
|
<div class="m-2 flex">You are selling {getTotal(getAllForRocket(rocket, selected))} Merits</div>
|
||||||
|
<div class="m-2 flex">
|
||||||
|
<Input bind:value={bitcoinAddress} type="text" placeholder="Bitcoin Address for Payment" class="m-1 max-w-xs" />
|
||||||
|
<Button on:click={()=>{publish(getAllForRocket(rocket, selected), bitcoinAddress, rocket)}} class="m-1">Sell Now</Button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{:else}<Login />{/if}
|
||||||
@@ -1,70 +1,31 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { ndk } from '@/ndk';
|
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import type { ExtendedBaseType, NDKEventStore } from '@nostr-dev-kit/ndk-svelte';
|
import type { ExtendedBaseType } from '@nostr-dev-kit/ndk-svelte';
|
||||||
import { onDestroy } from 'svelte';
|
import { type Readable } from 'svelte/store';
|
||||||
import { derived, type Readable } from 'svelte/store';
|
|
||||||
import Heading from '../../../components/Heading.svelte';
|
import Heading from '../../../components/Heading.svelte';
|
||||||
import RocketDashboard from '../../../components/RocketDashboard.svelte';
|
import RocketDashboard from '../../../components/RocketDashboard.svelte';
|
||||||
|
import Rocket from '../../../route_helpers/Rocket.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%.
|
||||||
|
|
||||||
let rIgnitionOrActual = $page.params.ignition;
|
let rIgnitionOrActual = $page.params.ignition;
|
||||||
let rName = $page.url.searchParams.get('d');
|
let dTag = $page.url.searchParams.get('d');
|
||||||
let rPubkey = $page.url.searchParams.get('p');
|
let pubkey = $page.url.searchParams.get('p');
|
||||||
|
|
||||||
let rocketEvents: NDKEventStore<NDKEvent> | undefined;
|
|
||||||
let latestRocketEvent: Readable<ExtendedBaseType<NDKEvent> | undefined>;
|
let latestRocketEvent: Readable<ExtendedBaseType<NDKEvent> | undefined>;
|
||||||
|
|
||||||
let candidateProducts: Readable<ExtendedBaseType<NDKEvent>[]>;
|
|
||||||
onDestroy(() => {
|
|
||||||
rocketEvents?.unsubscribe();
|
|
||||||
});
|
|
||||||
|
|
||||||
//if we don't have a d/p tags we just render the event provided
|
//if we don't have a d/p tags we just render the event provided
|
||||||
//todo: to find the latest by name alone we should use a new route and redirect to this page once we've got the relevant data
|
//todo: to find the latest by name alone we should use a new route and redirect to this page once we've got the relevant data
|
||||||
if (!rName && !rPubkey && rIgnitionOrActual.length == 64) {
|
if (!dTag && !pubkey && rIgnitionOrActual.length == 64) {
|
||||||
//this is the actual event ID the user wants to view so fetch the single event and render
|
//this is the actual event ID the user wants to view so fetch the single event and render
|
||||||
//warn user that this information is probably out of date and let them reroute to get the latest
|
//warn user that this information is probably out of date and let them reroute to get the latest
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rName && rPubkey) {
|
if (dTag && pubkey) {
|
||||||
//the user wants the latest valid state of this rocket
|
//the user wants the latest valid state of this rocket
|
||||||
rocketEvents = $ndk.storeSubscribe(
|
|
||||||
[
|
|
||||||
{ '#d': [rName], authors: [rPubkey], kinds: [31108 as number] },
|
|
||||||
{ '#a': [`31108:${rPubkey}:${rName}`] }
|
|
||||||
],
|
|
||||||
{ subId: rName }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (rocketEvents && !latestRocketEvent) {
|
|
||||||
latestRocketEvent = derived(rocketEvents, ($events) => {
|
|
||||||
let sorted = $events.filter((e) => {
|
|
||||||
return e.kind == 31108;
|
|
||||||
});
|
|
||||||
sorted = sorted.toSorted((a, b) => {
|
|
||||||
return a.created_at - b.created_at;
|
|
||||||
});
|
|
||||||
return sorted[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($latestRocketEvent && !candidateProducts) {
|
|
||||||
candidateProducts = derived(rocketEvents, ($events) => {
|
|
||||||
return $events.filter((e) => {
|
|
||||||
for (let p of $latestRocketEvent.getMatchingTags('product')) {
|
|
||||||
if (p[1].includes(e.id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e.kind == 1908;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: check that this zap is not already included in the payment JSON for the product
|
//todo: check that this zap is not already included in the payment JSON for the product
|
||||||
@@ -75,12 +36,15 @@
|
|||||||
|
|
||||||
//todo: handle shadow events (fetch the shadowed event and render it instead)
|
//todo: handle shadow events (fetch the shadowed event and render it instead)
|
||||||
</script>
|
</script>
|
||||||
|
{#if dTag && pubkey}
|
||||||
|
<Rocket bind:latestRocketEvent {dTag} {pubkey} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if latestRocketEvent && $latestRocketEvent}
|
{#if latestRocketEvent && $latestRocketEvent}
|
||||||
<RocketDashboard rocket={$latestRocketEvent} />
|
<RocketDashboard rocket={$latestRocketEvent} />
|
||||||
{:else}
|
{:else}
|
||||||
<Heading title="Fetching events for the requested rocket" />
|
<Heading title="Fetching events for the requested rocket" />
|
||||||
IGNITION: {rIgnitionOrActual} <br />
|
IGNITION: {rIgnitionOrActual} <br />
|
||||||
NAME: {rName} <br />
|
NAME: {dTag} <br />
|
||||||
PUBKEY: {rPubkey} <br />
|
PUBKEY: {pubkey} <br />
|
||||||
{/if}
|
{/if}
|
||||||
186
src/routes/sellmerits/+page.svelte
Normal file
186
src/routes/sellmerits/+page.svelte
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import * as Table from '@/components/ui/table';
|
||||||
|
import { type RocketAMR, AMRAuction, Rocket } from '@/event_helpers/rockets';
|
||||||
|
import { ndk } from '@/ndk';
|
||||||
|
import { currentUser } from '@/stores/session';
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
import { onDestroy } from 'svelte';
|
||||||
|
import { derived } from 'svelte/store';
|
||||||
|
import Login from '../../components/Login.svelte';
|
||||||
|
import MeritAuctions from '../../stateupdaters/MeritAuctions.svelte';
|
||||||
|
let rocketEvents = $ndk.storeSubscribe([{ kinds: [31108 as number] }], { subId: 'all_rockets' });
|
||||||
|
onDestroy(() => {
|
||||||
|
rocketEvents?.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
let rockets = derived(rocketEvents, ($rocketEvents) => {
|
||||||
|
let m = new Map<string, NDKEvent>();
|
||||||
|
for (let e of $rocketEvents) {
|
||||||
|
let existing = m.get(e.pubkey + e.dTag);
|
||||||
|
if (!existing) {
|
||||||
|
existing = e;
|
||||||
|
}
|
||||||
|
if (e.created_at > existing) {
|
||||||
|
existing = e;
|
||||||
|
}
|
||||||
|
m.set(e.pubkey + e.dTag, e);
|
||||||
|
}
|
||||||
|
return Array.from(m, ([_, e]) => e);
|
||||||
|
});
|
||||||
|
|
||||||
|
let myMeritRequests = derived([currentUser, rockets], ([$currentUser, $rockets]) => {
|
||||||
|
let merits = new Map<Rocket, RocketAMR[]>();
|
||||||
|
if ($currentUser) {
|
||||||
|
for (let r of $rockets) {
|
||||||
|
let parsedRocket = new Rocket(r);
|
||||||
|
let _merits: RocketAMR[] = [];
|
||||||
|
for (let [_, amr] of parsedRocket.ApprovedMeritRequests()) {
|
||||||
|
if (amr.Pubkey == $currentUser.pubkey) {
|
||||||
|
_merits.push(amr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merits.set(parsedRocket, _merits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merits;
|
||||||
|
});
|
||||||
|
|
||||||
|
let selected_amrs = new Map<string, AMRAuction>();
|
||||||
|
function toggleSelected(amr: RocketAMR, rocket: Rocket) {
|
||||||
|
if (!selected_amrs.has(rocket.Event.id)) {
|
||||||
|
selected_amrs.set(rocket.Event.id, new AMRAuction(rocket.Event))
|
||||||
|
}
|
||||||
|
let existing = selected_amrs.get(rocket.Event.id)!
|
||||||
|
if (existing.AMRIDs.includes(amr.ID)) {
|
||||||
|
existing.Pop(amr)
|
||||||
|
} else {
|
||||||
|
existing.Push(amr)
|
||||||
|
}
|
||||||
|
selected_amrs.set(rocket.Event.id, existing)
|
||||||
|
selected_amrs = selected_amrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedStatus(rocket: string, id:string, data: Map<string, AMRAuction>):boolean {
|
||||||
|
let has = false
|
||||||
|
let amr = data.get(rocket);
|
||||||
|
if (amr) {
|
||||||
|
has = amr.AMRIDs.includes(id)
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMerits(rocket:string, data:Map<string, AMRAuction>):number {
|
||||||
|
let m = data.get(rocket)
|
||||||
|
console.log(m)
|
||||||
|
let merits = 0
|
||||||
|
if (m && m.Merits) {
|
||||||
|
merits = m.Merits
|
||||||
|
}
|
||||||
|
return merits
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// function getTotal(auction:AMRAuction):number {
|
||||||
|
// let total = 0
|
||||||
|
// for (let [_, amr] of list) {
|
||||||
|
// total += amr.Merits
|
||||||
|
// }
|
||||||
|
// return total
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function getAllForRocket(rocket:Rocket, selected:Map<string, AMRAuction>):Map<string, AMRAuction> {
|
||||||
|
// let thisRocket = new Map<string, AMRAuction>()
|
||||||
|
// for (let [_, amr] of selected) {
|
||||||
|
// if (amr.RocketD == rocket.Name() && amr.RocketP == rocket.Event.author.pubkey) {
|
||||||
|
// thisRocket.set(amr.AMRID, amr)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return thisRocket
|
||||||
|
// }
|
||||||
|
|
||||||
|
function publish(amr:AMRAuction, rocket:Rocket) {
|
||||||
|
if (!$ndk.signer) {
|
||||||
|
throw new Error('no ndk signer found');
|
||||||
|
}
|
||||||
|
|
||||||
|
let author = $currentUser;
|
||||||
|
if (!author) {
|
||||||
|
throw new Error('no current user');
|
||||||
|
}
|
||||||
|
amr.RxAddress = bitcoinAddress
|
||||||
|
if (!amr.Validate()) {throw new Error("invalid")}
|
||||||
|
if (!amr.ValidateAgainstRocket(rocket)) {throw new Error("invalid against rocket")}
|
||||||
|
let e = amr.GenerateEvent()
|
||||||
|
e.ndk = $ndk
|
||||||
|
e.author = author;
|
||||||
|
e.publish().then((x) => {
|
||||||
|
console.log(x, e);
|
||||||
|
selected_amrs = new Map<string, AMRAuction>();
|
||||||
|
//goto(`${base}/rockets/${getRocketURL(e)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let bitcoinAddress:string = ""
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1 class=" m-2 text-nowrap text-center text-xl">Trade your Merits for Sats</h1>
|
||||||
|
|
||||||
|
{#if $currentUser}
|
||||||
|
{#each $myMeritRequests as [rocket, amr]}
|
||||||
|
{#if amr.length > 0}
|
||||||
|
<h1>ROCKET: {rocket.Name()}</h1>
|
||||||
|
|
||||||
|
<Table.Root>
|
||||||
|
<Table.Header>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Head class="w-[100px]">Selected</Table.Head>
|
||||||
|
<Table.Head class="w-[10px]">AMR</Table.Head>
|
||||||
|
<Table.Head class="w-[10px]">Elegible</Table.Head>
|
||||||
|
<Table.Head>Merits</Table.Head>
|
||||||
|
<Table.Head class="text-right">Sats (approx)</Table.Head>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
<Table.Body>
|
||||||
|
{#each amr as a, id (a.ID)}
|
||||||
|
<Table.Row class={getSelectedStatus(rocket.Event.id, a.ID, selected_amrs) ? 'bg-orange-500 hover:bg-orange-500' : ''}>
|
||||||
|
<Table.Cell
|
||||||
|
><Checkbox
|
||||||
|
id={a.ID}
|
||||||
|
checked={getSelectedStatus(rocket.Event.id, a.ID, selected_amrs)}
|
||||||
|
on:click={() => {
|
||||||
|
toggleSelected(a, rocket);
|
||||||
|
}}
|
||||||
|
/></Table.Cell
|
||||||
|
>
|
||||||
|
<Table.Cell
|
||||||
|
><span
|
||||||
|
class="cursor-pointer font-medium underline"
|
||||||
|
on:click={() => {
|
||||||
|
goto(`${base}/rockets/merits/${a.ID}`);
|
||||||
|
}}>{a.ID.substring(0, 6)}</span
|
||||||
|
></Table.Cell
|
||||||
|
>
|
||||||
|
<Table.Cell>{a.LeadTime == 0}</Table.Cell>
|
||||||
|
<Table.Cell>{a.Merits}</Table.Cell>
|
||||||
|
<Table.Cell class="text-right">{a.Merits}</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
{/each}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
{#if selected_amrs.get(rocket.Event.id)}
|
||||||
|
<div class="m-2 flex">You are selling {selected_amrs.get(rocket.Event.id)?.Merits} Merits</div>
|
||||||
|
<div class="m-2 flex">
|
||||||
|
<Input bind:value={bitcoinAddress} type="text" placeholder="Bitcoin Address for Payment" class="m-1 max-w-xs" />
|
||||||
|
<Button on:click={()=>{publish(selected_amrs.get(rocket.Event.id), rocket)}} class="m-1">Sell Now</Button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{:else}<Login />{/if}
|
||||||
|
<MeritAuctions />
|
||||||
43
src/stateupdaters/MeritAuctions.svelte
Normal file
43
src/stateupdaters/MeritAuctions.svelte
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { AMRAuction, Rocket } from "@/event_helpers/rockets";
|
||||||
|
import { ndk } from "@/ndk";
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
import { derived } from "svelte/store";
|
||||||
|
|
||||||
|
let saleEvents = $ndk.storeSubscribe([{ kinds: [1412 as number] }], { subId: 'all_sale_requests' });
|
||||||
|
onDestroy(() => {
|
||||||
|
saleEvents?.unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
export let rockets:Set<Rocket>|undefined = undefined;
|
||||||
|
|
||||||
|
let validAuctionRequests = derived(saleEvents, ($saleEvents) =>{
|
||||||
|
let provisional = new Map<string, AMRAuction>()
|
||||||
|
let valid = new Map<string, AMRAuction>()
|
||||||
|
for (let e of $saleEvents) {
|
||||||
|
let a = new AMRAuction(undefined, e)
|
||||||
|
|
||||||
|
if (a.Validate()) {
|
||||||
|
provisional.set(a.Event.id, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (rockets) {
|
||||||
|
for (let [_, a] of provisional) {
|
||||||
|
for (let r of rockets) {
|
||||||
|
if (r.Name() == a.RocketD && a.RocketP == r.Event.pubkey) {
|
||||||
|
if (a.ValidateAgainstRocket(r)) {
|
||||||
|
valid.set(a.Event.id, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {valid = provisional}
|
||||||
|
return valid
|
||||||
|
})
|
||||||
|
validAuctionRequests.subscribe(requests=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each $validAuctionRequests as [_, a]}<span on:click={()=>{console.log(a)}}>{a.Event.id}</span>{/each}
|
||||||
Reference in New Issue
Block a user