diff --git a/packages/app/src/Element/LiveChat.css b/packages/app/src/Element/LiveChat.css
new file mode 100644
index 00000000..5423adf5
--- /dev/null
+++ b/packages/app/src/Element/LiveChat.css
@@ -0,0 +1,11 @@
+.live-chat {
+ height: calc(100vh - 73px);
+ display: flex;
+ flex-direction: column;
+}
+
+.live-chat > div:nth-child(1) {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column-reverse;
+}
diff --git a/packages/app/src/Element/LiveChat.tsx b/packages/app/src/Element/LiveChat.tsx
new file mode 100644
index 00000000..306dc9cd
--- /dev/null
+++ b/packages/app/src/Element/LiveChat.tsx
@@ -0,0 +1,63 @@
+import "./LiveChat.css";
+import { NostrLink, TaggedRawEvent } from "@snort/system";
+import { useUserProfile } from "@snort/system-react";
+import { useState } from "react";
+import Textarea from "./Textarea";
+import { useLiveChatFeed } from "Feed/LiveChatFeed";
+import useEventPublisher from "Feed/EventPublisher";
+import { System } from "index";
+import { getDisplayName } from "Element/ProfileImage";
+
+export function LiveChat({ ev, link }: { ev: TaggedRawEvent; link: NostrLink }) {
+ const [chat, setChat] = useState("");
+ const messages = useLiveChatFeed(link);
+ const pub = useEventPublisher();
+
+ async function sendChatMessage() {
+ const reply = await pub?.note(chat, eb => {
+ return eb.tag(["a", `${link.kind}:${link.author}:${link.id}`]);
+ });
+ if (reply) {
+ console.debug(reply);
+ System.BroadcastEvent(reply);
+ }
+ setChat("");
+ }
+ return (
+
+
+ {[...(messages.data ?? [])]
+ .sort((a, b) => b.created_at - a.created_at)
+ .map(a => (
+
+ ))}
+
+
+
+
+ );
+}
+
+function ChatMessage({ ev }: { ev: TaggedRawEvent }) {
+ const profile = useUserProfile(System, ev.pubkey);
+ return (
+
+ {getDisplayName(profile, ev.pubkey)}
+ :
+ {ev.content}
+
+ );
+}
diff --git a/packages/app/src/Element/LiveEvent.tsx b/packages/app/src/Element/LiveEvent.tsx
index 576c9a64..5c134e52 100644
--- a/packages/app/src/Element/LiveEvent.tsx
+++ b/packages/app/src/Element/LiveEvent.tsx
@@ -1,11 +1,25 @@
-import { NostrEvent } from "@snort/system";
-import { findTag } from "SnortUtils";
-import { LiveVideoPlayer } from "Element/LiveVideoPlayer";
+import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
+import { findTag, unwrap } from "SnortUtils";
+import { FormattedMessage } from "react-intl";
+import { Link } from "react-router-dom";
export function LiveEvent({ ev }: { ev: NostrEvent }) {
- const stream = findTag(ev, "streaming");
- if (stream) {
- return ;
- }
- return null;
+ const title = findTag(ev, "title");
+ const d = unwrap(findTag(ev, "d"));
+ return (
+
+
+
+
{title}
+
+
+
+
+
+
+
+
+ );
}
diff --git a/packages/app/src/Element/LiveVideoPlayer.tsx b/packages/app/src/Element/LiveVideoPlayer.tsx
index c7e58bf5..19bf767d 100644
--- a/packages/app/src/Element/LiveVideoPlayer.tsx
+++ b/packages/app/src/Element/LiveVideoPlayer.tsx
@@ -1,19 +1,19 @@
import Hls from "hls.js";
import { HTMLProps, useEffect, useRef } from "react";
-export function LiveVideoPlayer(props: HTMLProps) {
+export function LiveVideoPlayer(props: HTMLProps & { stream: string }) {
const video = useRef(null);
useEffect(() => {
- if (props.src && video.current && !video.current.src && Hls.isSupported()) {
+ if (props.stream && video.current && !video.current.src && Hls.isSupported()) {
const hls = new Hls();
- hls.loadSource(props.src);
+ hls.loadSource(props.stream);
hls.attachMedia(video.current);
return () => hls.destroy();
}
}, [video, props]);
return (
-
-
+
+
);
}
diff --git a/packages/app/src/Element/MediaElement.tsx b/packages/app/src/Element/MediaElement.tsx
index 2f50b04d..73f2300b 100644
--- a/packages/app/src/Element/MediaElement.tsx
+++ b/packages/app/src/Element/MediaElement.tsx
@@ -174,7 +174,7 @@ export function MediaElement(props: MediaElementProps) {
return