diff --git a/package.json b/package.json index e7f9ad0..82a5d39 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@webscopeio/react-textarea-autocomplete": "^4.9.2", "hls.js": "^1.4.6", "lodash": "^4.17.21", + "moment": "^2.29.4", "qr-code-styling": "^1.6.0-rc.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/element/new-stream.tsx b/src/element/new-stream.tsx index 7a15400..fa397a5 100644 --- a/src/element/new-stream.tsx +++ b/src/element/new-stream.tsx @@ -5,15 +5,9 @@ import { EventPublisher, NostrEvent } from "@snort/system"; import { unixNow } from "@snort/shared"; import AsyncButton from "./async-button"; -import { System } from "index"; +import { StreamState, System } from "index"; import { findTag } from "utils"; -enum StreamState { - Live = "live", - Ended = "ended", - Planned = "planned" -} - export function NewStream({ ev, onFinish, @@ -26,6 +20,7 @@ export function NewStream({ const [image, setImage] = useState(findTag(ev, "image") ?? ""); const [stream, setStream] = useState(findTag(ev, "streaming") ?? ""); const [status, setStatus] = useState(findTag(ev, "status") ?? StreamState.Live); + const [start, setStart] = useState(findTag(ev, "starts")); const [isValid, setIsValid] = useState(false); function validate() { @@ -51,7 +46,7 @@ export function NewStream({ const evNew = await pub.generic((eb) => { const now = unixNow(); const dTag = findTag(ev, "d") ?? now.toString(); - const starts = findTag(ev, "starts") ?? now.toString(); + const starts = start ?? now.toString(); const ends = findTag(ev, "ends") ?? now.toString(); eb .kind(30_311) @@ -73,6 +68,16 @@ export function NewStream({ } } + function toDateTimeString(n: number) { + console.debug(n); + return new Date(n * 1000).toISOString().substring(0, -1) + } + + function fromDateTimeString(s: string) { + console.debug(s); + return Math.floor(new Date(s).getTime() / 1000) + } + return (

{ev ? "Edit Stream" : "New Stream"}

@@ -129,6 +134,12 @@ export function NewStream({ )}
+ {status === StreamState.Planned &&
+

Start Time

+
+ setStart(fromDateTimeString(e.target.value).toString())} /> +
+
}
{ const b = new RequestBuilder(`event:${link.id.slice(0, 12)}`); + b.withOptions({ + leaveOpen + }) if (link.type === NostrPrefix.Address) { const f = b.withFilter().tag("d", [link.id]); if (link.author) { diff --git a/src/index.css b/src/index.css index d3feb4a..d5e6082 100644 --- a/src/index.css +++ b/src/index.css @@ -83,7 +83,7 @@ a { gap: 8px; } -input[type="text"], textarea { +input[type="text"], textarea, input[type="datetime-local"] { font-family: inherit; border: unset; background-color: unset; diff --git a/src/index.tsx b/src/index.tsx index 9453d28..893f68b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -11,6 +11,12 @@ import { StreamPage } from "pages/stream-page"; import { ChatPopout } from "pages/chat-popout"; import { LoginStore } from "login"; +export enum StreamState { + Live = "live", + Ended = "ended", + Planned = "planned" +} + export const System = new NostrSystem({}); export const Login = new LoginStore(); diff --git a/src/pages/root.css b/src/pages/root.css index 2f29338..2f1ab52 100644 --- a/src/pages/root.css +++ b/src/pages/root.css @@ -15,4 +15,10 @@ .video-grid { grid-template-columns: repeat(8, 1fr); } +} + +.homepage h2 { + + background: #171717; + padding: 40px; } \ No newline at end of file diff --git a/src/pages/root.tsx b/src/pages/root.tsx index a4b9dad..456edc7 100644 --- a/src/pages/root.tsx +++ b/src/pages/root.tsx @@ -4,14 +4,16 @@ import { useMemo } from "react"; import { unixNow } from "@snort/shared"; import { EventKind, ParameterizedReplaceableNoteStore, RequestBuilder } from "@snort/system"; import { useRequestBuilder } from "@snort/system-react"; -import { System } from ".."; +import { StreamState, System } from ".."; import { VideoTile } from "../element/video-tile"; import { findTag } from "utils"; export function RootPage() { const rb = useMemo(() => { const rb = new RequestBuilder("root"); - rb.withFilter() + rb.withOptions({ + leaveOpen: true + }).withFilter() .kinds([30_311 as EventKind]) .since(unixNow() - 86400); return rb; @@ -32,7 +34,21 @@ export function RootPage() { } return []; }, [feed.data]) - return
- {feedSorted.map(e => )} + + const live = feedSorted.filter(a => findTag(a, "status") === StreamState.Live); + const planned = feedSorted.filter(a => findTag(a, "status") === StreamState.Planned); + const ended = feedSorted.filter(a => findTag(a, "status") === StreamState.Ended); + return
+
+ {live.map(e => )} +
+ {planned.length > 0 && <>

Planned

+
+ {planned.map(e => )} +
} + {ended.length > 0 && <>

Ended

+
+ {ended.map(e => )} +
}
} \ No newline at end of file diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index d96cba9..6c09092 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -26,11 +26,11 @@ font-size: 14px; line-height: 18px; color: #A7A7A7; + text-transform: uppercase; } .live-page .pill.live { color: inherit; - text-transform: uppercase; } .live-page .info { diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index 6355377..85c920f 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -2,6 +2,7 @@ import "./stream-page.css"; import { useState } from "react"; import { parseNostrLink, EventPublisher } from "@snort/system"; import { useNavigate, useParams } from "react-router-dom"; +import moment from "moment"; import useEventFeed from "hooks/event-feed"; import { LiveVideoPlayer } from "element/live-video-player"; @@ -11,7 +12,7 @@ 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"; +import { StreamState, System } from "index"; import Modal from "element/modal"; import { SendZaps } from "element/send-zap"; import { useUserProfile } from "@snort/system-react"; @@ -20,7 +21,7 @@ import { NewStream } from "element/new-stream"; export function StreamPage() { const params = useParams(); const link = parseNostrLink(params.id!); - const thisEvent = useEventFeed(link); + const thisEvent = useEventFeed(link, true); const login = useLogin(); const navigate = useNavigate(); const [zap, setZap] = useState(false); @@ -30,6 +31,7 @@ export function StreamPage() { const stream = findTag(thisEvent.data, "streaming"); const status = findTag(thisEvent.data, "status"); const image = findTag(thisEvent.data, "image"); + const start = findTag(thisEvent.data, "starts"); const isLive = status === "live"; const isMine = link.author === login?.pubkey; const zapTarget = profile?.lud16 ?? profile?.lud06; @@ -54,6 +56,7 @@ export function StreamPage() {

{findTag(thisEvent.data, "summary")}

{status} + {status === StreamState.Planned && Starts {moment(Number(start) * 1000).fromNow()}} {thisEvent.data?.tags .filter((a) => a[0] === "t") .map((a) => a[1]) @@ -111,7 +114,7 @@ export function StreamPage() { setEdit(false)}> window.location.reload()} + onFinish={() => { }} /> )} diff --git a/yarn.lock b/yarn.lock index 9b10797..bb6e718 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6574,6 +6574,11 @@ mkdirp@~0.5.1: dependencies: minimist "^1.2.6" +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"