-
+
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
+
+
+
+
+}
\ 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
-
- {(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 <>
-