Add planned start time

This commit is contained in:
Kieran 2023-06-25 23:33:34 +01:00
parent ec2ef2142b
commit 602b4c5d0d
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
10 changed files with 69 additions and 18 deletions

View File

@ -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",

View File

@ -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"

View File

@ -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) {

View File

@ -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;

View File

@ -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();

View File

@ -15,4 +15,10 @@
.video-grid {
grid-template-columns: repeat(8, 1fr);
}
}
.homepage h2 {
background: #171717;
padding: 40px;
}

View File

@ -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>
}

View File

@ -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 {

View File

@ -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>
)}

View File

@ -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"