diff --git a/public/icons.svg b/public/icons.svg index f683e4f..1571504 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -49,6 +49,10 @@ - + + + + + diff --git a/src/element/send-zap.tsx b/src/element/send-zap.tsx index c56eb8e..d8f0ce6 100644 --- a/src/element/send-zap.tsx +++ b/src/element/send-zap.tsx @@ -215,7 +215,7 @@ export function SendZapsDialog(props: Omit) { ) : ( Zap - + )} diff --git a/src/element/share-menu.tsx b/src/element/share-menu.tsx new file mode 100644 index 0000000..f04aa4a --- /dev/null +++ b/src/element/share-menu.tsx @@ -0,0 +1,89 @@ +import { Menu, MenuItem } from "@szhsin/react-menu"; +import * as Dialog from "@radix-ui/react-dialog"; +import { Icon } from "./icon"; +import { useState } from "react"; +import { Textarea } from "./textarea"; +import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system"; +import { findTag } from "utils"; +import AsyncButton from "./async-button"; +import { useLogin } from "hooks/login"; +import { System } from "index"; + +type ShareOn = "nostr" | "twitter"; + +export function ShareMenu({ ev }: { ev: NostrEvent }) { + const [share, setShare] = useState(); + const [message, setMessage] = useState(""); + const login = useLogin(); + + const naddr = encodeTLV( + NostrPrefix.Address, + findTag(ev, "d")!, + undefined, + ev.kind, + ev.pubkey + ); + const link = `https://zap.stream/${naddr}`; + + async function sendMessage() { + const pub = login?.publisher(); + if (pub) { + const ev = await pub.note(message); + console.debug(ev); + System.BroadcastEvent(ev); + setShare(undefined); + } + } + + return ( + <> + + Share + + } + > + { + setMessage( + `Come check out my stream on zap.stream!\n\n${link}\n\nnostr:${naddr}` + ); + setShare("nostr"); + }} + > + + Broadcast on Nostr + + + setShare(undefined)} + > + + + + Share + + setMessage(e.target.value)} + onKeyDown={() => { + //noop + }} + rows={15} + /> + + + Send + + + + + > + ); +} diff --git a/src/element/textarea.tsx b/src/element/textarea.tsx index ca353c6..e11b55b 100644 --- a/src/element/textarea.tsx +++ b/src/element/textarea.tsx @@ -1,6 +1,8 @@ import "./textarea.css"; import type { KeyboardEvent, ChangeEvent } from "react"; -import ReactTextareaAutocomplete, { TriggerType } from "@webscopeio/react-textarea-autocomplete"; +import ReactTextareaAutocomplete, { + TriggerType, +} from "@webscopeio/react-textarea-autocomplete"; import "@webscopeio/react-textarea-autocomplete/style.css"; import uniqWith from "lodash/uniqWith"; import isEqual from "lodash/isEqual"; @@ -41,6 +43,7 @@ interface TextareaProps { value: string; onChange: (e: ChangeEvent) => void; onKeyDown: (e: KeyboardEvent) => void; + rows?: number; } export function Textarea({ emojis, ...props }: TextareaProps) { diff --git a/src/index.css b/src/index.css index e31f23e..3012582 100644 --- a/src/index.css +++ b/src/index.css @@ -62,6 +62,12 @@ a { gap: 12px; } +.w-max { + width: stretch; + width: -webkit-fill-available; + width: -moz-available; +} + .btn { border: none; outline: none; @@ -71,6 +77,10 @@ a { line-height: 20px; padding: 8px 16px; border-radius: 16px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; } .btn-border { @@ -95,6 +105,11 @@ a { opacity: 0.9; } +.btn-secondary { + color: white; + background: #222; +} + .btn-warning { background: #ff563f; color: white; @@ -171,10 +186,29 @@ div.paper { border: 1px solid #ff563f; } +.dialog-overlay { + background-color: rgba(0, 0, 0, 0.8); + position: fixed; + inset: 0; + z-index: 1; +} + .dialog-content { display: flex; flex-direction: column; gap: 12px; + z-index: 2; + background-color: #171717; + border-radius: 24px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 90vw; + max-width: 450px; + max-height: 85vh; + padding: 25px; + overflow-y: auto; } .dialog-content div.paper { diff --git a/src/pages/layout.css b/src/pages/layout.css index 6476656..4168b96 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -177,28 +177,6 @@ button span.hide-on-mobile { } } -.dialog-overlay { - background-color: rgba(0, 0, 0, 0.8); - position: fixed; - inset: 0; - z-index: 1; -} - -.dialog-content { - z-index: 2; - 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; - overflow-y: auto; -} - .zap-icon { color: #FF8D2B; } diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index a89853a..814d8c0 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -2,7 +2,6 @@ grid-area: main-content; } - .video-content video { max-height: 230px; width: 100vw; @@ -18,7 +17,11 @@ display: flex; justify-content: space-between; padding: 0 16px; - width: 100%; + gap: 24px; +} + +.profile-info .btn { + padding: 12px 16px; } @media (min-width: 768px) { @@ -39,7 +42,7 @@ font-weight: 700; font-size: 14px; line-height: 18px; - color: #A7A7A7; + color: #a7a7a7; } .pill.live { @@ -49,7 +52,7 @@ .pill.viewers { color: white; - background: rgba(23, 23, 23, 0.70); + background: rgba(23, 23, 23, 0.7); } @media (min-width: 1020px) { @@ -96,7 +99,7 @@ .info { grid-area: profile; - margin-top: 8px + margin-top: 8px; } .info h1 { @@ -116,13 +119,6 @@ gap: 12px; } -.info .btn.zap { - padding: 12px 16px; - display: flex; - align-items: center; - gap: 12px; -} - .offline { background: rgba(0, 0, 0, 0.5); display: flex; @@ -130,11 +126,11 @@ align-items: center; } -.online>div { +.online > div { display: none; } -.offline>div { +.offline > div { position: fixed; top: 5em; text-transform: uppercase; @@ -143,12 +139,12 @@ } @media (min-width: 768px) { - .offline>div { + .offline > div { top: 10em; } } -.offline>video { +.offline > video { z-index: -1; position: relative; } diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index 978eb14..0c34f0a 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -19,6 +19,7 @@ import { Tags } from "element/tags"; import { StatePill } from "element/state-pill"; import { formatSats } from "number"; import { StreamTimer } from "element/stream-time"; +import { ShareMenu } from "element/share-menu"; function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) { const login = useLogin(); @@ -74,17 +75,24 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) { )} - + - {zapTarget && ev && ( - - )} + + {ev && ( + <> + + {zapTarget && ( + + )} + > + )} + >