From c301d4b9c48ce4bd0bc923fcedb76dc3c8a64dd6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 22 Jun 2023 11:40:25 +0100 Subject: [PATCH] Create stream modal --- src/element/live-chat.css | 6 --- src/element/live-chat.tsx | 7 ++- src/element/modal.css | 22 +++++++++ src/element/modal.tsx | 26 +++++++++++ src/element/new-stream.css | 39 ++++++++++++++++ src/element/new-stream.tsx | 96 ++++++++++++++++++++++++++++++++++++++ src/element/profile.tsx | 8 ++-- src/index.css | 25 ++++++++++ src/pages/layout.css | 15 ------ src/pages/layout.tsx | 11 ++++- src/pages/stream-page.css | 4 ++ src/pages/stream-page.tsx | 25 +++++++++- 12 files changed, 254 insertions(+), 30 deletions(-) create mode 100644 src/element/modal.css create mode 100644 src/element/modal.tsx create mode 100644 src/element/new-stream.css create mode 100644 src/element/new-stream.tsx diff --git a/src/element/live-chat.css b/src/element/live-chat.css index db8231c..afd0307 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -30,12 +30,6 @@ } .live-chat>.write-message>div:nth-child(1) { - background: #171717; - border-radius: 16px; - padding: 8px 16px; - display: flex; - gap: 10px; - align-items: center; height: 32px; flex-grow: 1; } diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index 070649e..0264344 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -40,7 +40,7 @@ export function LiveChat({ link, options }: { link: NostrLink, options?: LiveCha function writeMessage() { return <> -
+
- + zapped   {parsed.amount} diff --git a/src/element/modal.css b/src/element/modal.css new file mode 100644 index 0000000..b8181bf --- /dev/null +++ b/src/element/modal.css @@ -0,0 +1,22 @@ +.modal { + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.8); + display: flex; + justify-content: center; + z-index: 42; + overflow-y: auto; +} + +.modal-body { + display: flex; + width: 430px; + padding: 32px; + margin-top: auto; + margin-bottom: auto; + border-radius: 32px; + background: #171717; +} \ No newline at end of file diff --git a/src/element/modal.tsx b/src/element/modal.tsx new file mode 100644 index 0000000..8f7f7ca --- /dev/null +++ b/src/element/modal.tsx @@ -0,0 +1,26 @@ +import "./modal.css"; +import { useEffect, MouseEventHandler, ReactNode } from "react"; + +export interface ModalProps { + className?: string; + onClose?: MouseEventHandler; + children: ReactNode; +} + +export default function Modal(props: ModalProps) { + const onClose = props.onClose || (() => undefined); + const className = props.className || ""; + + useEffect(() => { + document.body.classList.add("scroll-lock"); + return () => document.body.classList.remove("scroll-lock"); + }, []); + + return ( +
+
e.stopPropagation()}> + {props.children} +
+
+ ); +} diff --git a/src/element/new-stream.css b/src/element/new-stream.css new file mode 100644 index 0000000..a0af867 --- /dev/null +++ b/src/element/new-stream.css @@ -0,0 +1,39 @@ +.new-stream { + display: flex; + flex-direction: column; + gap: 24px; + width: inherit; +} + +.new-stream h3 { + font-size: 24px; + margin: 0; +} + +.new-stream div.input { + background: #262626; + height: 32px; +} + +.new-stream p { + margin: 0 0 8px 0; +} + +.new-stream small { + display: block; + margin: 8px 0 0 0; +} + +.new-stream .btn { + padding: 12px 16px; + border-radius: 16px; + width: 100%; +} + +.new-stream .btn>span { + justify-content: center; +} + +.new-stream .btn:disabled { + opacity: 0.3; +} \ No newline at end of file diff --git a/src/element/new-stream.tsx b/src/element/new-stream.tsx new file mode 100644 index 0000000..efc7f1e --- /dev/null +++ b/src/element/new-stream.tsx @@ -0,0 +1,96 @@ +import { useEffect, useState } from "react"; +import { EventPublisher } from "@snort/system"; +import { unixNow } from "@snort/shared"; +import "./new-stream.css"; +import AsyncButton from "./async-button"; +import { System } from "index"; + +export function NewStream() { + const [title, setTitle] = useState(""); + const [summary, setSummary] = useState(""); + const [image, setImage] = useState(""); + const [stream, setStream] = useState(""); + const [isValid, setIsValid] = useState(false); + + function validate() { + if (title.length < 2) { + return false; + } + if (stream.length < 5 || !stream.match(/^https?:\/\/.*\.m3u8?$/i)) { + return false; + } + if (image.length > 0 && !image.match(/^https?:\/\//i)) { + return false; + } + return true; + } + + useEffect(() => { + setIsValid(validate()); + }, [title, summary, image, stream]); + + async function publishStream() { + const pub = await EventPublisher.nip7(); + if (pub) { + const ev = await pub.generic(eb => { + const now = unixNow(); + return eb.kind(30_311) + .tag(["d", now.toString()]) + .tag(["title", title]) + .tag(["summary", summary]) + .tag(["image", image]) + .tag(["streaming", stream]) + .tag(["status", "live"]) + }); + console.debug(ev); + System.BroadcastEvent(ev); + } + } + + return
+

+ New Stream +

+ +
+

+ Title +

+
+ setTitle(e.target.value)} /> +
+
+
+

+ Summary +

+
+ setSummary(e.target.value)} /> +
+
+
+

+ Cover image +

+
+ setImage(e.target.value)} /> +
+
+
+

+ Stream Url +

+
+ setStream(e.target.value)} /> +
+ + Stream type should be HLS + +
+
+ + Start Stream + +
+
+} \ No newline at end of file diff --git a/src/element/profile.tsx b/src/element/profile.tsx index dce4296..d5131c8 100644 --- a/src/element/profile.tsx +++ b/src/element/profile.tsx @@ -5,8 +5,10 @@ import { hexToBech32 } from "@snort/shared"; import { System } from "index"; export interface ProfileOptions { - showName?: boolean, + showName?: boolean + showAvatar?: boolean suffix?: string + overrideName?: string } export function getName(pk: string, user?: UserMetadata) { @@ -18,7 +20,7 @@ export function Profile({ pubkey, options }: { pubkey: string, options?: Profile const profile = useUserProfile(System, pubkey); return
- Profile - {(options?.showName ?? true) && getName(pubkey, profile)} + {(options?.showAvatar ?? true) && } + {(options?.showName ?? true) && (options?.overrideName ?? getName(pubkey, profile))}
} \ No newline at end of file diff --git a/src/index.css b/src/index.css index 9d55b60..592996d 100644 --- a/src/index.css +++ b/src/index.css @@ -70,4 +70,29 @@ a { display: flex; align-items: center; gap: 8px; +} + +input[type="text"] { + font-family: inherit; + border: unset; + background-color: unset; + color: inherit; + width: 100%; + font-size: 16px; + font-weight: 500; + outline: none; +} + +div.input { + background: #171717; + border-radius: 16px; + padding: 8px 16px; + display: flex; + gap: 10px; + align-items: center; +} + +.scroll-lock { + overflow: hidden; + height: 100vh; } \ No newline at end of file diff --git a/src/pages/layout.css b/src/pages/layout.css index 0419167..d3b6546 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -20,12 +20,7 @@ header>div:nth-child(1) { } header>div:nth-child(2) { - background: #171717; - border-radius: 16px; - padding: 8px 16px; min-width: 300px; - display: flex; - align-items: center; height: 32px; } @@ -35,16 +30,6 @@ header>div:nth-child(3) { gap: 24px; } -header input[type="text"] { - border: unset; - background-color: unset; - color: inherit; - width: 100%; - font-size: 16px; - font-weight: 500; - outline: none; -} - header input[type="text"]:active { border: unset; } diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 603ce17..3861ac2 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -6,10 +6,14 @@ import AsyncButton from "element/async-button"; import { Login } from "index"; import { useLogin } from "hooks/login"; import { Profile } from "element/profile"; +import Modal from "element/modal"; +import { NewStream } from "element/new-stream"; +import { useState } from "react"; export function LayoutPage() { const navigate = useNavigate(); const login = useLogin(); + const [newStream, setNewStream] = useState(false); async function doLogin() { const pub = await EventPublisher.nip7(); @@ -22,7 +26,7 @@ export function LayoutPage() { if (!login) return; return <> - @@ -48,7 +52,7 @@ export function LayoutPage() {
navigate("/")}> S
-
+
@@ -58,5 +62,8 @@ export function LayoutPage() {
+ {newStream && setNewStream(false)} > + + } } \ No newline at end of file diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index b4e1c3f..a79723b 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -50,4 +50,8 @@ .live-page .tags { display: flex; gap: 8px; +} + +.live-page .actions { + margin: 8px 0 0 0; } \ No newline at end of file diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index cd77ef5..224ec40 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -1,6 +1,6 @@ import "./stream-page.css"; -import { parseNostrLink } from "@snort/system"; -import { useParams } from "react-router-dom"; +import { parseNostrLink, EventPublisher } from "@snort/system"; +import { useNavigate, useParams } from "react-router-dom"; import useEventFeed from "hooks/event-feed"; import { LiveVideoPlayer } from "element/live-video-player"; @@ -9,15 +9,31 @@ import { Profile } from "element/profile"; import { LiveChat } from "element/live-chat"; import AsyncButton from "element/async-button"; import { Icon } from "element/icon"; +import { useLogin } from "hooks/login"; +import { System } from "index"; export function StreamPage() { const params = useParams(); const link = parseNostrLink(params.id!); const thisEvent = useEventFeed(link); + const login = useLogin(); + const navigate = useNavigate(); const stream = findTag(thisEvent.data, "streaming"); const status = findTag(thisEvent.data, "status"); const isLive = status === "live"; + const isMine = link.author === login?.pubkey; + + async function deleteStream() { + const pub = await EventPublisher.nip7(); + if (pub && thisEvent.data) { + const ev = await pub.delete(thisEvent.data.id); + console.debug(ev); + System.BroadcastEvent(ev); + navigate("/"); + } + } + return (
@@ -39,6 +55,11 @@ export function StreamPage() { ))}
+
+ {isMine && + Delete + } +