snort/packages/app/src/Pages/ZapPool.tsx

223 lines
6.5 KiB
TypeScript
Raw Normal View History

2023-05-16 21:30:52 +00:00
import "./ZapPool.css";
import { useMemo, useSyncExternalStore } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
2023-06-16 19:31:33 +00:00
import { useUserProfile } from "@snort/system-react";
2023-05-16 21:30:52 +00:00
import { SnortPubKey } from "Const";
2023-09-28 09:26:10 +00:00
import ProfilePreview from "Element/User/ProfilePreview";
2023-05-16 21:30:52 +00:00
import useLogin from "Hooks/useLogin";
import { UploaderServices } from "Upload";
2023-05-24 10:12:23 +00:00
import { bech32ToHex, getRelayName, unwrap } from "SnortUtils";
2023-05-16 21:30:52 +00:00
import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController";
import AsyncButton from "Element/AsyncButton";
import { useWallet } from "Wallet";
2023-10-13 15:34:31 +00:00
import useEventPublisher from "Hooks/useEventPublisher";
2023-05-16 21:30:52 +00:00
2023-05-18 09:29:27 +00:00
const DataProviders = [
{
name: "nostr.band",
owner: bech32ToHex("npub1sx9rnd03vs34lp39fvfv5krwlnxpl90f3dzuk8y3cuwutk2gdhdqjz6g8m"),
},
2023-05-18 10:42:30 +00:00
{
name: "semisol.dev",
owner: bech32ToHex("npub12262qa4uhw7u8gdwlgmntqtv7aye8vdcmvszkqwgs0zchel6mz7s6cgrkj"),
},
2023-05-18 09:29:27 +00:00
{
name: "nostr.watch",
owner: bech32ToHex("npub1uac67zc9er54ln0kl6e4qp2y6ta3enfcg7ywnayshvlw9r5w6ehsqq99rx"),
},
{
name: "nostr.directory",
owner: bech32ToHex("npub1teawtzxh6y02cnp9jphxm2q8u6xxfx85nguwg6ftuksgjctvavvqnsgq5u"),
},
];
2023-05-16 21:30:52 +00:00
function ZapTarget({ target }: { target: ZapPoolRecipient }) {
const login = useLogin();
2023-08-24 14:25:54 +00:00
const profile = useUserProfile(target.pubkey);
2023-05-16 21:30:52 +00:00
const hasAddress = profile?.lud16 || profile?.lud06;
const defaultZapMount = Math.ceil(login.preferences.defaultZapAmount * (target.split / 100));
return (
<ProfilePreview
pubkey={target.pubkey}
actions={
hasAddress ? (
<div>
<div>
<FormattedNumber value={target.split} />% (
<FormattedMessage defaultMessage="{n} sats" values={{ n: defaultZapMount }} />)
</div>
<input
type="range"
min={0}
max={100}
value={target.split}
onChange={e =>
ZapPoolController.set({
...target,
split: e.target.valueAsNumber,
})
}
/>
</div>
) : (
<FormattedMessage defaultMessage="No lightning address" />
)
}
/>
);
}
export default function ZapPoolPage() {
const login = useLogin();
2023-10-13 15:34:31 +00:00
const { system } = useEventPublisher();
2023-05-16 21:30:52 +00:00
const zapPool = useSyncExternalStore(
c => ZapPoolController.hook(c),
2023-09-12 21:58:37 +00:00
() => ZapPoolController.snapshot(),
2023-05-16 21:30:52 +00:00
);
const { wallet } = useWallet();
const relayConnections = useMemo(() => {
2023-10-13 15:34:31 +00:00
return system.Sockets.map(a => {
2023-06-01 08:54:25 +00:00
if (a.info?.pubkey && !a.ephemeral) {
return {
address: a.address,
pubkey: a.info.pubkey,
};
}
})
2023-05-16 21:30:52 +00:00
.filter(a => a !== undefined)
.map(unwrap);
}, [login.relays]);
const sumPending = zapPool.reduce((acc, v) => acc + v.sum, 0);
return (
2023-08-21 13:58:57 +00:00
<div className="zap-pool main-content p">
2023-05-16 21:30:52 +00:00
<h1>
<FormattedMessage defaultMessage="Zap Pool" />
</h1>
<p>
<FormattedMessage defaultMessage="Fund the services that you use by splitting a portion of all your zaps into a pool of funds!" />
</p>
<p>
<FormattedMessage defaultMessage="Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)" />
</p>
<p>
<FormattedMessage
defaultMessage="Your default zap amount is {number} sats, example values are calculated from this."
values={{
number: (
<b>
<FormattedNumber value={login.preferences.defaultZapAmount} />
</b>
),
}}
/>
</p>
<p>
<FormattedMessage
defaultMessage="A single zap of {nIn} sats will allocate {nOut} sats to the zap pool."
values={{
nIn: (
<b>
<FormattedNumber value={login.preferences.defaultZapAmount} />
</b>
),
nOut: (
<b>
<FormattedNumber value={ZapPoolController.calcAllocation(login.preferences.defaultZapAmount)} />
</b>
),
}}
/>
</p>
<p>
<FormattedMessage
defaultMessage="You currently have {number} sats in your zap pool."
values={{
number: (
<b>
<FormattedNumber value={sumPending} />
</b>
),
}}
/>
</p>
<p>
{wallet && (
<AsyncButton onClick={() => ZapPoolController.payout(wallet)}>
<FormattedMessage defaultMessage="Payout Now" />
</AsyncButton>
)}
</p>
2023-08-21 13:58:57 +00:00
<div>
2023-05-16 21:30:52 +00:00
<ZapTarget
target={
zapPool.find(b => b.pubkey === bech32ToHex(SnortPubKey) && b.type === ZapPoolRecipientType.Generic) ?? {
type: ZapPoolRecipientType.Generic,
pubkey: bech32ToHex(SnortPubKey),
split: 0,
sum: 0,
}
}
/>
</div>
<h3>
<FormattedMessage defaultMessage="Relays" />
</h3>
{relayConnections.map(a => (
2023-08-21 13:58:57 +00:00
<div>
2023-05-16 21:30:52 +00:00
<h4>{getRelayName(a.address)}</h4>
<ZapTarget
target={
zapPool.find(b => b.pubkey === a.pubkey && b.type === ZapPoolRecipientType.Relay) ?? {
type: ZapPoolRecipientType.Relay,
pubkey: a.pubkey,
split: 0,
sum: 0,
}
}
/>
</div>
))}
<h3>
<FormattedMessage defaultMessage="File hosts" />
</h3>
{UploaderServices.map(a => (
2023-08-21 13:58:57 +00:00
<div>
2023-05-16 21:30:52 +00:00
<h4>{a.name}</h4>
<ZapTarget
target={
zapPool.find(b => b.pubkey === a.owner && b.type === ZapPoolRecipientType.FileHost) ?? {
type: ZapPoolRecipientType.FileHost,
pubkey: a.owner,
split: 0,
sum: 0,
}
}
/>
</div>
))}
2023-05-18 09:29:27 +00:00
<h3>
<FormattedMessage defaultMessage="Data Providers" />
</h3>
{DataProviders.map(a => (
2023-08-21 13:58:57 +00:00
<div>
2023-05-18 09:29:27 +00:00
<h4>{a.name}</h4>
<ZapTarget
target={
zapPool.find(b => b.pubkey === a.owner && b.type === ZapPoolRecipientType.DataProvider) ?? {
type: ZapPoolRecipientType.DataProvider,
pubkey: a.owner,
split: 0,
sum: 0,
}
}
/>
</div>
))}
2023-05-16 21:30:52 +00:00
</div>
);
}