Update live stream page
This commit is contained in:
parent
9f00157bdc
commit
fc2304c9fa
@ -1,11 +1,46 @@
|
||||
.live-chat {
|
||||
height: calc(100vh - 73px);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.live-chat > div:nth-child(1) {
|
||||
font-size: 24px;
|
||||
line-height: 29px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.live-chat > div:nth-child(2) {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-direction: column-reverse;
|
||||
margin-block-end: 20px;
|
||||
}
|
||||
|
||||
.live-chat > div:nth-child(3) {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.live-chat .message {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.live-chat .message .name {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: var(--highlight);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.live-chat .message .avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
@ -1,30 +1,47 @@
|
||||
import "./LiveChat.css";
|
||||
import { NostrLink, TaggedRawEvent } from "@snort/system";
|
||||
import { EventKind, NostrLink, TaggedRawEvent } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { useState } from "react";
|
||||
import Textarea from "./Textarea";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Textarea from "Element/Textarea";
|
||||
import { useLiveChatFeed } from "Feed/LiveChatFeed";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
import { System } from "index";
|
||||
import { getDisplayName } from "Element/ProfileImage";
|
||||
import Avatar from "Element/Avatar";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import Text from "Element/Text";
|
||||
import { System } from "index";
|
||||
import { profileLink } from "SnortUtils";
|
||||
|
||||
export function LiveChat({ ev, link }: { ev: TaggedRawEvent; link: NostrLink }) {
|
||||
const [chat, setChat] = useState("");
|
||||
const messages = useLiveChatFeed(link);
|
||||
const pub = useEventPublisher();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
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);
|
||||
if (chat.length > 1) {
|
||||
const reply = await pub?.generic(eb => {
|
||||
return eb
|
||||
.kind(1311 as EventKind)
|
||||
.content(chat)
|
||||
.tag(["a", `${link.kind}:${link.author}:${link.id}`])
|
||||
.processContent();
|
||||
});
|
||||
if (reply) {
|
||||
console.debug(reply);
|
||||
System.BroadcastEvent(reply);
|
||||
}
|
||||
setChat("");
|
||||
}
|
||||
setChat("");
|
||||
}
|
||||
return (
|
||||
<div className="live-chat">
|
||||
<div>
|
||||
<FormattedMessage defaultMessage="Stream Chat" />
|
||||
</div>
|
||||
<div>
|
||||
{[...(messages.data ?? [])]
|
||||
.sort((a, b) => b.created_at - a.created_at)
|
||||
@ -39,13 +56,19 @@ export function LiveChat({ ev, link }: { ev: TaggedRawEvent; link: NostrLink })
|
||||
onChange={v => setChat(v.target.value)}
|
||||
value={chat}
|
||||
onFocus={() => {}}
|
||||
placeholder=""
|
||||
placeholder={formatMessage({
|
||||
defaultMessage: "Message...",
|
||||
})}
|
||||
onKeyDown={async e => {
|
||||
if (e.code === "Enter") {
|
||||
e.preventDefault();
|
||||
await sendChatMessage();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<AsyncButton onClick={sendChatMessage}>
|
||||
<FormattedMessage defaultMessage="Send" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -53,11 +76,17 @@ export function LiveChat({ ev, link }: { ev: TaggedRawEvent; link: NostrLink })
|
||||
|
||||
function ChatMessage({ ev }: { ev: TaggedRawEvent }) {
|
||||
const profile = useUserProfile(System, ev.pubkey);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<b>{getDisplayName(profile, ev.pubkey)}</b>
|
||||
:
|
||||
{ev.content}
|
||||
<div className="message">
|
||||
<div className="name" onClick={() => navigate(profileLink(ev.pubkey, ev.relays))}>
|
||||
<Avatar user={profile} />
|
||||
{getDisplayName(profile, ev.pubkey)}:
|
||||
</div>
|
||||
<span>
|
||||
<Text disableMedia={true} content={ev.content} creator={ev.pubkey} tags={ev.tags} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ export default function Text({ content, tags, creator, disableMedia, depth }: Te
|
||||
};
|
||||
|
||||
if (validateLink()) {
|
||||
if (disableMedia ?? false) {
|
||||
if ((disableMedia ?? false) && !a.startsWith("nostr:")) {
|
||||
return (
|
||||
<a href={a} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
|
||||
{a}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FlatNoteStore, NostrLink, RequestBuilder } from "@snort/system";
|
||||
import { EventKind, FlatNoteStore, NostrLink, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { System } from "index";
|
||||
import { useMemo } from "react";
|
||||
@ -10,6 +10,7 @@ export function useLiveChatFeed(link: NostrLink) {
|
||||
leaveOpen: true,
|
||||
});
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.ZapReceipt, 1311 as EventKind])
|
||||
.tag("a", [`${link.kind}:${link.author}:${link.id}`])
|
||||
.limit(100);
|
||||
return rb;
|
||||
|
@ -1,8 +1,31 @@
|
||||
.live-page {
|
||||
display: grid;
|
||||
grid-template-columns: auto 250px;
|
||||
height: calc(100% - 105px);
|
||||
padding: 24px;
|
||||
grid-template-columns: auto 350px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (min-width: 2000px) {
|
||||
.live-page {
|
||||
grid-template-columns: auto 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.live-page > div:nth-child(1) {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.live-page video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16/9;
|
||||
}
|
||||
|
||||
.live-page .pill {
|
||||
padding: 4px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ import { findTag, unwrap } from "SnortUtils";
|
||||
import PageSpinner from "Element/PageSpinner";
|
||||
import { LiveChat } from "Element/LiveChat";
|
||||
import useEventFeed from "Feed/EventFeed";
|
||||
import ProfilePreview from "Element/ProfilePreview";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import Icon from "Icons/Icon";
|
||||
|
||||
export function LivePage() {
|
||||
const params = useParams();
|
||||
@ -20,8 +24,40 @@ export function LivePage() {
|
||||
return (
|
||||
<div className="live-page main-content">
|
||||
<div>
|
||||
<h3>{findTag(thisEvent.data, "title")}</h3>
|
||||
<LiveVideoPlayer stream={unwrap(findTag(thisEvent.data, "streaming"))} autoPlay={true} />
|
||||
<div className="flex">
|
||||
<div className="f-grow">
|
||||
<h1>{findTag(thisEvent.data, "title")}</h1>
|
||||
<p>{findTag(thisEvent.data, "summary")}</p>
|
||||
<div>
|
||||
{thisEvent.data?.tags
|
||||
.filter(a => a[0] === "t")
|
||||
.map(a => a[1])
|
||||
.map(a => (
|
||||
<div className="pill" key={a}>
|
||||
{a}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ProfilePreview
|
||||
pubkey={thisEvent.data.pubkey}
|
||||
className="g10"
|
||||
options={{
|
||||
about: false,
|
||||
}}
|
||||
actions={
|
||||
<div className="flex">
|
||||
<AsyncButton onClick={() => {}}>
|
||||
<Icon name="zap" size={16} className="mr5" />
|
||||
<FormattedMessage defaultMessage="Zap" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LiveChat ev={thisEvent.data} link={link} />
|
||||
</div>
|
||||
|
@ -498,6 +498,14 @@ small.xs {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.g5 {
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.g10 {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user