forked from Kieran/zap.stream
Add planned start time
This commit is contained in:
parent
ec2ef2142b
commit
602b4c5d0d
@ -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",
|
||||
|
@ -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 (
|
||||
<div className="new-stream">
|
||||
<h3>{ev ? "Edit Stream" : "New Stream"}</h3>
|
||||
@ -129,6 +134,12 @@ export function NewStream({
|
||||
</span>)}
|
||||
</div>
|
||||
</div>
|
||||
{status === StreamState.Planned && <div>
|
||||
<p>Start Time</p>
|
||||
<div className="input">
|
||||
<input type="datetime-local" value={toDateTimeString(Number(start ?? "0"))} onChange={e => setStart(fromDateTimeString(e.target.value).toString())} />
|
||||
</div>
|
||||
</div>}
|
||||
<div>
|
||||
<AsyncButton
|
||||
type="button"
|
||||
|
@ -4,9 +4,12 @@ import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { System } from "index";
|
||||
|
||||
export default function useEventFeed(link: NostrLink) {
|
||||
export default function useEventFeed(link: NostrLink, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -15,4 +15,10 @@
|
||||
.video-grid {
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.homepage h2 {
|
||||
|
||||
background: #171717;
|
||||
padding: 40px;
|
||||
}
|
@ -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 <div className="video-grid">
|
||||
{feedSorted.map(e => <VideoTile ev={e} key={e.id} />)}
|
||||
|
||||
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 <div className="homepage">
|
||||
<div className="video-grid">
|
||||
{live.map(e => <VideoTile ev={e} key={e.id} />)}
|
||||
</div>
|
||||
{planned.length > 0 && <><h2>Planned</h2>
|
||||
<div className="video-grid">
|
||||
{planned.map(e => <VideoTile ev={e} key={e.id} />)}
|
||||
</div></>}
|
||||
{ended.length > 0 && <><h2>Ended</h2>
|
||||
<div className="video-grid">
|
||||
{ended.map(e => <VideoTile ev={e} key={e.id} />)}
|
||||
</div></>}
|
||||
</div>
|
||||
}
|
@ -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 {
|
||||
|
@ -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() {
|
||||
<p>{findTag(thisEvent.data, "summary")}</p>
|
||||
<div className="tags">
|
||||
<span className={`pill${isLive ? " live" : ""}`}>{status}</span>
|
||||
{status === StreamState.Planned && <span className="pill">Starts {moment(Number(start) * 1000).fromNow()}</span>}
|
||||
{thisEvent.data?.tags
|
||||
.filter((a) => a[0] === "t")
|
||||
.map((a) => a[1])
|
||||
@ -111,7 +114,7 @@ export function StreamPage() {
|
||||
<Modal onClose={() => setEdit(false)}>
|
||||
<NewStream
|
||||
ev={thisEvent.data}
|
||||
onFinish={() => window.location.reload()}
|
||||
onFinish={() => { }}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user