add sats per minute button

This commit is contained in:
hzrd149 2023-08-28 11:04:39 -05:00
parent 8ea8c88c52
commit 343a23c945
5 changed files with 131 additions and 3 deletions
.changeset
src
components
views/streams

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add sats per minute button on stream view on desktop

@ -349,3 +349,15 @@ export const BookmarkedIcon = createIcon({
d: "M4 2H20C20.5523 2 21 2.44772 21 3V22.2763C21 22.5525 20.7761 22.7764 20.5 22.7764C20.4298 22.7764 20.3604 22.7615 20.2963 22.7329L12 19.0313L3.70373 22.7329C3.45155 22.8455 3.15591 22.7322 3.04339 22.4801C3.01478 22.4159 3 22.3465 3 22.2763V3C3 2.44772 3.44772 2 4 2ZM12 13.5L14.9389 15.0451L14.3776 11.7725L16.7553 9.45492L13.4695 8.97746L12 6L10.5305 8.97746L7.24472 9.45492L9.62236 11.7725L9.06107 15.0451L12 13.5Z",
defaultProps,
});
export const PlayIcon = createIcon({
displayName: "PlayIcon",
d: "M16.3944 11.9998L10 7.73686V16.2628L16.3944 11.9998ZM19.376 12.4158L8.77735 19.4816C8.54759 19.6348 8.23715 19.5727 8.08397 19.3429C8.02922 19.2608 8 19.1643 8 19.0656V4.93408C8 4.65794 8.22386 4.43408 8.5 4.43408C8.59871 4.43408 8.69522 4.4633 8.77735 4.51806L19.376 11.5838C19.6057 11.737 19.6678 12.0474 19.5146 12.2772C19.478 12.3321 19.4309 12.3792 19.376 12.4158Z",
defaultProps,
});
export const StopIcon = createIcon({
displayName: "StopIcon",
d: "M7 7V17H17V7H7ZM6 5H18C18.5523 5 19 5.44772 19 6V18C19 18.5523 18.5523 19 18 19H6C5.44772 19 5 18.5523 5 18V6C5 5.44772 5.44772 5 6 5Z",
defaultProps,
});

@ -14,13 +14,15 @@ import {
Text,
useToast,
} from "@chakra-ui/react";
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
import dayjs from "dayjs";
import { Kind } from "nostr-tools";
import { useForm } from "react-hook-form";
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
import { UserAvatar } from "./user-avatar";
import { UserLink } from "./user-link";
import { parsePaymentRequest, readablizeSats } from "../helpers/bolt11";
import { LightningIcon } from "./icons";
import { Kind } from "nostr-tools";
import clientRelaysService from "../services/client-relays";
import { getEventRelays } from "../services/event-relays";
import { useSigningContext } from "../providers/signing-provider";
@ -30,7 +32,6 @@ import useUserLNURLMetadata from "../hooks/use-user-lnurl-metadata";
import { requestZapInvoice } from "../helpers/zaps";
import { ParsedStream, getATag } from "../helpers/nostr/stream";
import EmbeddedNote from "./note/embedded-note";
import dayjs from "dayjs";
import { unique } from "../helpers/array";
import { useUserRelays } from "../hooks/use-user-relays";
import { RelayMode } from "../classes/relay";

@ -0,0 +1,108 @@
import { useCallback, useState } from "react";
import {
Button,
Flex,
FlexProps,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverHeader,
PopoverTrigger,
Spinner,
Text,
} from "@chakra-ui/react";
import { useInterval } from "react-use";
import useUserLNURLMetadata from "../../../hooks/use-user-lnurl-metadata";
import { parsePaymentRequest } from "../../../helpers/bolt11";
import { PlayIcon, StopIcon } from "../../../components/icons";
export default function StreamSatsPerMinute({ pubkey, ...props }: { pubkey: string } & FlexProps) {
const [enabled, setEnabled] = useState(false);
const [paying, setPaying] = useState(false);
const [amountStr, setAmountStr] = useState("21");
const { metadata } = useUserLNURLMetadata(pubkey);
const isAvailable = !!window.webln;
const isEnabled = isAvailable && enabled && !!metadata?.callback;
const sendSats = useCallback(async () => {
if (isEnabled && window.webln) {
try {
setPaying(true);
if (!window.webln.enabled) await window.webln.enable();
const amountMsats = parseInt(amountStr) * 1000;
if (!Number.isFinite(amountMsats)) throw new Error("invalid amount");
const callbackUrl = new URL(metadata.callback);
callbackUrl.searchParams.append("amount", String(amountMsats));
const { pr: payRequest } = await fetch(callbackUrl).then((res) => res.json());
if (payRequest as string) {
const parsed = parsePaymentRequest(payRequest);
if (parsed.amount !== amountMsats) throw new Error("incorrect amount");
} else throw new Error("Failed to get invoice");
await window.webln.sendPayment(payRequest);
} catch (e) {
setEnabled(false);
}
setPaying(false);
}
}, [setPaying, metadata?.callback, enabled, isEnabled]);
useInterval(sendSats, 1000 * 60);
return (
<Flex gap="2">
<Popover>
<PopoverTrigger>
<Button rightIcon={isEnabled ? <Spinner size="sm" /> : undefined}>Stream sats</Button>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Stream {amountStr} sats per minute</PopoverHeader>
<PopoverBody>
{isAvailable ? (
<Flex gap="2">
<NumberInput
step={1}
min={1}
value={amountStr}
onChange={(v) => setAmountStr(v)}
isDisabled={!isAvailable}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Button
leftIcon={isEnabled ? <StopIcon /> : <PlayIcon />}
onClick={() => setEnabled((v) => !v)}
isDisabled={!isAvailable}
>
{isEnabled ? "Stop" : "Start"}
</Button>
</Flex>
) : (
<Text colorScheme="orange">Missing WebLN</Text>
)}
</PopoverBody>
</PopoverContent>
</Popover>
</Flex>
);
}

@ -34,6 +34,7 @@ import RelaySelectionButton from "../../../components/relay-selection/relay-sele
import RelaySelectionProvider from "../../../providers/relay-selection-provider";
import StreamerCards from "../components/streamer-cards";
import { useAppTitle } from "../../../hooks/use-app-title";
import StreamSatsPerMinute from "../components/stream-sats-per-minute";
function StreamPage({ stream, displayMode }: { stream: ParsedStream; displayMode?: ChatDisplayMode }) {
useAppTitle(stream.title);
@ -122,6 +123,7 @@ function StreamPage({ stream, displayMode }: { stream: ParsedStream; displayMode
<Text>{stream.title}</Text>
</Box>
<Spacer />
{!!window.webln && <StreamSatsPerMinute pubkey={stream.host} />}
<StreamDebugButton stream={stream} variant="ghost" />
<RelaySelectionButton />
<Button onClick={() => navigate(-1)}>Back</Button>