forked from Kieran/zap.stream
feat: responsible zap modal
This commit is contained in:
parent
391e549421
commit
713f4c3d5e
@ -49,22 +49,3 @@
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
background: #353535;
|
background: #353535;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-overlay {
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
background-color: #171717;
|
|
||||||
border-radius: 6px;
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 90vw;
|
|
||||||
max-width: 450px;
|
|
||||||
max-height: 85vh;
|
|
||||||
padding: 25px;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./new-stream.css";
|
import "./new-stream.css";
|
||||||
import * as Dialog from "@radix-ui/react-dialog";
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { EventPublisher, NostrEvent } from "@snort/system";
|
import { EventPublisher, NostrEvent } from "@snort/system";
|
||||||
import { unixNow } from "@snort/shared";
|
import { unixNow } from "@snort/shared";
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export function NewStream({
|
|||||||
onFinish,
|
onFinish,
|
||||||
}: {
|
}: {
|
||||||
ev?: NostrEvent;
|
ev?: NostrEvent;
|
||||||
onFinish: (ev: NostrEvent) => void;
|
onFinish?: (ev: NostrEvent) => void;
|
||||||
}) {
|
}) {
|
||||||
const [title, setTitle] = useState(findTag(ev, "title") ?? "");
|
const [title, setTitle] = useState(findTag(ev, "title") ?? "");
|
||||||
const [summary, setSummary] = useState(findTag(ev, "summary") ?? "");
|
const [summary, setSummary] = useState(findTag(ev, "summary") ?? "");
|
||||||
@ -27,7 +27,7 @@ export function NewStream({
|
|||||||
const [start, setStart] = useState(findTag(ev, "starts"));
|
const [start, setStart] = useState(findTag(ev, "starts"));
|
||||||
const [isValid, setIsValid] = useState(false);
|
const [isValid, setIsValid] = useState(false);
|
||||||
|
|
||||||
function validate() {
|
const validate = useCallback(() => {
|
||||||
if (title.length < 2) {
|
if (title.length < 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -38,11 +38,11 @@ export function NewStream({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}, [title, image, stream]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsValid(validate());
|
setIsValid(validate());
|
||||||
}, [title, summary, image, stream]);
|
}, [validate, title, summary, image, stream]);
|
||||||
|
|
||||||
async function publishStream() {
|
async function publishStream() {
|
||||||
const pub = await EventPublisher.nip7();
|
const pub = await EventPublisher.nip7();
|
||||||
@ -67,7 +67,7 @@ export function NewStream({
|
|||||||
});
|
});
|
||||||
console.debug(evNew);
|
console.debug(evNew);
|
||||||
System.BroadcastEvent(evNew);
|
System.BroadcastEvent(evNew);
|
||||||
onFinish(evNew);
|
onFinish && onFinish(evNew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ interface NewStreamDialogProps {
|
|||||||
text?: string;
|
text?: string;
|
||||||
btnClassName?: string;
|
btnClassName?: string;
|
||||||
ev?: NostrEvent;
|
ev?: NostrEvent;
|
||||||
onFinish: (e: NostrEvent) => void;
|
onFinish?: (e: NostrEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NewStreamDialog({
|
export function NewStreamDialog({
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
.send-zap {
|
.send-zap {
|
||||||
width: inherit;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -58,4 +57,4 @@
|
|||||||
|
|
||||||
.send-zap .qr {
|
.send-zap .qr {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import "./send-zap.css";
|
import "./send-zap.css";
|
||||||
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { LNURL } from "@snort/shared";
|
import { LNURL } from "@snort/shared";
|
||||||
import { NostrEvent, EventPublisher } from "@snort/system";
|
import { NostrEvent, EventPublisher } from "@snort/system";
|
||||||
@ -9,17 +10,14 @@ import { findTag } from "utils";
|
|||||||
import { Relays } from "index";
|
import { Relays } from "index";
|
||||||
import QrCode from "./qr-code";
|
import QrCode from "./qr-code";
|
||||||
|
|
||||||
export function SendZaps({
|
interface SendZapsProps {
|
||||||
lnurl,
|
|
||||||
ev,
|
|
||||||
targetName,
|
|
||||||
onFinish,
|
|
||||||
}: {
|
|
||||||
lnurl: string;
|
lnurl: string;
|
||||||
ev?: NostrEvent;
|
ev?: NostrEvent;
|
||||||
targetName?: string;
|
targetName?: string;
|
||||||
onFinish: () => void;
|
onFinish: () => void;
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
function SendZaps({ lnurl, ev, targetName, onFinish }: SendZapsProps) {
|
||||||
const UsdRate = 30_000;
|
const UsdRate = 30_000;
|
||||||
|
|
||||||
const satsAmounts = [
|
const satsAmounts = [
|
||||||
@ -152,6 +150,31 @@ export function SendZaps({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SendZapDialog() {
|
export function SendZapsDialog({
|
||||||
return "TODO";
|
lnurl,
|
||||||
|
ev,
|
||||||
|
targetName,
|
||||||
|
}: Omit<SendZapsProps, "onFinish">) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<Dialog.Trigger asChild>
|
||||||
|
<button className="btn btn-primary zap">
|
||||||
|
<span className="hide-on-mobile">Zap</span>
|
||||||
|
<Icon name="zap" size={16} />
|
||||||
|
</button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay className="dialog-overlay" />
|
||||||
|
<Dialog.Content className="dialog-content">
|
||||||
|
<SendZaps
|
||||||
|
lnurl={lnurl}
|
||||||
|
ev={ev}
|
||||||
|
targetName={targetName}
|
||||||
|
onFinish={() => setIsOpen(false)}
|
||||||
|
/>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Portal>
|
||||||
|
</Dialog.Root>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -162,3 +162,22 @@ button span.hide-on-mobile {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-overlay {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
background-color: #171717;
|
||||||
|
border-radius: 6px;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 90vw;
|
||||||
|
max-width: 450px;
|
||||||
|
max-height: 85vh;
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
@ -35,6 +35,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.homepage {
|
||||||
|
width: 100%;
|
||||||
|
grid-area: main-content;
|
||||||
|
}
|
||||||
|
|
||||||
.homepage h2 {
|
.homepage h2 {
|
||||||
background: #171717;
|
background: #171717;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./stream-page.css";
|
import "./stream-page.css";
|
||||||
import { useRef, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { parseNostrLink, EventPublisher } from "@snort/system";
|
import { parseNostrLink, EventPublisher } from "@snort/system";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
@ -10,11 +10,9 @@ import { findTag } from "utils";
|
|||||||
import { Profile, getName } from "element/profile";
|
import { Profile, getName } from "element/profile";
|
||||||
import { LiveChat } from "element/live-chat";
|
import { LiveChat } from "element/live-chat";
|
||||||
import AsyncButton from "element/async-button";
|
import AsyncButton from "element/async-button";
|
||||||
import { Icon } from "element/icon";
|
|
||||||
import { useLogin } from "hooks/login";
|
import { useLogin } from "hooks/login";
|
||||||
import { StreamState, System } from "index";
|
import { StreamState, System } from "index";
|
||||||
import Modal from "element/modal";
|
import { SendZapsDialog } from "element/send-zap";
|
||||||
import { SendZaps } from "element/send-zap";
|
|
||||||
import type { NostrLink } from "@snort/system";
|
import type { NostrLink } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { NewStreamDialog } from "element/new-stream";
|
import { NewStreamDialog } from "element/new-stream";
|
||||||
@ -23,7 +21,6 @@ function ProfileInfo({ link }: { link: NostrLink }) {
|
|||||||
const thisEvent = useEventFeed(link, true);
|
const thisEvent = useEventFeed(link, true);
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [zap, setZap] = useState(false);
|
|
||||||
const profile = useUserProfile(System, thisEvent.data?.pubkey);
|
const profile = useUserProfile(System, thisEvent.data?.pubkey);
|
||||||
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
||||||
|
|
||||||
@ -42,8 +39,6 @@ function ProfileInfo({ link }: { link: NostrLink }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFinish() {}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex info">
|
<div className="flex info">
|
||||||
@ -69,11 +64,7 @@ function ProfileInfo({ link }: { link: NostrLink }) {
|
|||||||
{isMine && (
|
{isMine && (
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
{thisEvent.data && (
|
{thisEvent.data && (
|
||||||
<NewStreamDialog
|
<NewStreamDialog text="Edit" ev={thisEvent.data} />
|
||||||
text="Edit"
|
|
||||||
ev={thisEvent.data}
|
|
||||||
onFinish={onFinish}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
type="button"
|
type="button"
|
||||||
@ -87,22 +78,15 @@ function ProfileInfo({ link }: { link: NostrLink }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="profile-info flex g24">
|
<div className="profile-info flex g24">
|
||||||
<Profile pubkey={thisEvent.data?.pubkey ?? ""} />
|
<Profile pubkey={thisEvent.data?.pubkey ?? ""} />
|
||||||
<button onClick={() => setZap(true)} className="btn btn-primary zap">
|
{zapTarget && thisEvent.data && (
|
||||||
<span className="hide-on-mobile">Zap</span>
|
<SendZapsDialog
|
||||||
<Icon name="zap" size={16} />
|
lnurl={zapTarget}
|
||||||
</button>
|
ev={thisEvent.data}
|
||||||
|
targetName={getName(thisEvent.data.pubkey, profile)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{zap && zapTarget && thisEvent.data && (
|
|
||||||
<Modal onClose={() => setZap(false)}>
|
|
||||||
<SendZaps
|
|
||||||
lnurl={zapTarget}
|
|
||||||
ev={thisEvent.data}
|
|
||||||
targetName={getName(thisEvent.data.pubkey, profile)}
|
|
||||||
onFinish={() => setZap(false)}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user