chore: Update translations
This commit is contained in:
@ -11,18 +11,19 @@ import NoteAppHandler from "../Event/Note/NoteAppHandler";
|
|||||||
import ProfileImage from "../User/ProfileImage";
|
import ProfileImage from "../User/ProfileImage";
|
||||||
const LiveKitRoom = lazy(() => import("./livekit"));
|
const LiveKitRoom = lazy(() => import("./livekit"));
|
||||||
|
|
||||||
|
|
||||||
export function LiveEvent({ ev }: { ev: TaggedNostrEvent }) {
|
export function LiveEvent({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
const service = ev.tags.find(a => a[0] === "streaming")?.at(1);
|
const service = ev.tags.find(a => a[0] === "streaming")?.at(1);
|
||||||
function inner() {
|
function inner() {
|
||||||
if (service?.endsWith(".m3u8")) {
|
if (service?.endsWith(".m3u8")) {
|
||||||
return <LiveStreamEvent ev={ev} />
|
return <LiveStreamEvent ev={ev} />;
|
||||||
} else if (service?.startsWith("wss+livekit://")) {
|
} else if (service?.startsWith("wss+livekit://")) {
|
||||||
return <Suspense>
|
return (
|
||||||
|
<Suspense>
|
||||||
<LiveKitRoom ev={ev} canJoin={true} />
|
<LiveKitRoom ev={ev} canJoin={true} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <NoteAppHandler ev={ev} />
|
return <NoteAppHandler ev={ev} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inner();
|
return inner();
|
||||||
|
@ -15,27 +15,26 @@ import Avatar from "../User/Avatar";
|
|||||||
import { AvatarGroup } from "../User/AvatarGroup";
|
import { AvatarGroup } from "../User/AvatarGroup";
|
||||||
import DisplayName from "../User/DisplayName";
|
import DisplayName from "../User/DisplayName";
|
||||||
|
|
||||||
export default function LiveKitRoom({ ev, canJoin }: { ev: TaggedNostrEvent, canJoin?: boolean }) {
|
export default function LiveKitRoom({ ev, canJoin }: { ev: TaggedNostrEvent; canJoin?: boolean }) {
|
||||||
const { stream, service, id } = extractStreamInfo(ev);
|
const { stream, service, id } = extractStreamInfo(ev);
|
||||||
const { publisher } = useEventPublisher();
|
const { publisher } = useEventPublisher();
|
||||||
const [join, setJoin] = useState(false);
|
const [join, setJoin] = useState(false);
|
||||||
const [token, setToken] = useState<string>();
|
const [token, setToken] = useState<string>();
|
||||||
|
|
||||||
async function getToken() {
|
async function getToken() {
|
||||||
if (!service || !publisher)
|
if (!service || !publisher) return;
|
||||||
return;
|
|
||||||
const url = `${service}/api/v1/nests/${id}`;
|
const url = `${service}/api/v1/nests/${id}`;
|
||||||
const auth = await publisher.generic(eb => {
|
const auth = await publisher.generic(eb => {
|
||||||
eb.kind(EventKind.HttpAuthentication);
|
eb.kind(EventKind.HttpAuthentication);
|
||||||
eb.tag(["url", url]);
|
eb.tag(["url", url]);
|
||||||
eb.tag(["u", url])
|
eb.tag(["u", url]);
|
||||||
eb.tag(["method", "GET"]);
|
eb.tag(["method", "GET"]);
|
||||||
return eb;
|
return eb;
|
||||||
});
|
});
|
||||||
const rsp = await fetch(url, {
|
const rsp = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Nostr ${window.btoa(JSON.stringify(auth))}`,
|
authorization: `Nostr ${window.btoa(JSON.stringify(auth))}`,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const text = await rsp.text();
|
const text = await rsp.text();
|
||||||
@ -46,72 +45,87 @@ export default function LiveKitRoom({ ev, canJoin }: { ev: TaggedNostrEvent, can
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (join && !token) {
|
if (join && !token) {
|
||||||
getToken().then(t => setToken(t?.token)).catch(console.error);
|
getToken()
|
||||||
|
.then(t => setToken(t?.token))
|
||||||
|
.catch(console.error);
|
||||||
}
|
}
|
||||||
}, [join]);
|
}, [join]);
|
||||||
|
|
||||||
if (!join) {
|
if (!join) {
|
||||||
return <div className="p flex flex-col gap-2">
|
return (
|
||||||
|
<div className="p flex flex-col gap-2">
|
||||||
<RoomHeader ev={ev} />
|
<RoomHeader ev={ev} />
|
||||||
{(canJoin ?? false) && <AsyncButton onClick={() => setJoin(true)}>
|
{(canJoin ?? false) && (
|
||||||
|
<AsyncButton onClick={() => setJoin(true)}>
|
||||||
<FormattedMessage defaultMessage="Join Room" />
|
<FormattedMessage defaultMessage="Join Room" />
|
||||||
</AsyncButton>}
|
</AsyncButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <LiveKitRoomContext token={token} serverUrl={stream?.replace("wss+livekit://", "wss://")} connect={true}>
|
return (
|
||||||
|
<LiveKitRoomContext token={token} serverUrl={stream?.replace("wss+livekit://", "wss://")} connect={true}>
|
||||||
<RoomAudioRenderer volume={1} />
|
<RoomAudioRenderer volume={1} />
|
||||||
<ParticipantList ev={ev} />
|
<ParticipantList ev={ev} />
|
||||||
</LiveKitRoomContext>
|
</LiveKitRoomContext>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RoomHeader({ ev }: { ev: TaggedNostrEvent }) {
|
function RoomHeader({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
const { image, title } = extractStreamInfo(ev);
|
const { image, title } = extractStreamInfo(ev);
|
||||||
return <div className="relative rounded-xl h-[140px] w-full overflow-hidden">
|
return (
|
||||||
{image ? <ProxyImg src={image} className="w-full" /> :
|
<div className="relative rounded-xl h-[140px] w-full overflow-hidden">
|
||||||
<div className="absolute bg-gray-dark w-full h-full" />}
|
{image ? <ProxyImg src={image} className="w-full" /> : <div className="absolute bg-gray-dark w-full h-full" />}
|
||||||
<div className="absolute left-4 top-4 w-full flex justify-between pr-4">
|
<div className="absolute left-4 top-4 w-full flex justify-between pr-4">
|
||||||
<div className="text-2xl">
|
<div className="text-2xl">{title}</div>
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<NostrParticipants ev={ev} />
|
<NostrParticipants ev={ev} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ParticipantList({ ev }: { ev: TaggedNostrEvent }) {
|
function ParticipantList({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
const participants = useParticipants()
|
const participants = useParticipants();
|
||||||
return <div className="p">
|
return (
|
||||||
|
<div className="p">
|
||||||
<RoomHeader ev={ev} />
|
<RoomHeader ev={ev} />
|
||||||
<h3>
|
<h3>
|
||||||
<FormattedMessage defaultMessage="Participants" />
|
<FormattedMessage defaultMessage="Participants" />
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-4">
|
<div className="grid grid-cols-4">
|
||||||
{participants.map(a => <LiveKitUser p={a} key={a.identity} />)}
|
{participants.map(a => (
|
||||||
|
<LiveKitUser p={a} key={a.identity} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NostrParticipants({ ev }: { ev: TaggedNostrEvent }) {
|
function NostrParticipants({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
const link = NostrLink.fromEvent(ev);
|
const link = NostrLink.fromEvent(ev);
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
const sub = new RequestBuilder(`livekit-participants:${link.tagKey}`);
|
const sub = new RequestBuilder(`livekit-participants:${link.tagKey}`);
|
||||||
sub.withFilter().replyToLink([link]).kinds([10_312 as EventKind]).since(unixNow() - 600);
|
sub
|
||||||
|
.withFilter()
|
||||||
|
.replyToLink([link])
|
||||||
|
.kinds([10_312 as EventKind])
|
||||||
|
.since(unixNow() - 600);
|
||||||
return sub;
|
return sub;
|
||||||
}, [link.tagKey]);
|
}, [link.tagKey]);
|
||||||
|
|
||||||
const presense = useRequestBuilder(sub);
|
const presense = useRequestBuilder(sub);
|
||||||
return <AvatarGroup ids={dedupe(presense.map(a => a.pubkey))} size={32} />
|
return <AvatarGroup ids={dedupe(presense.map(a => a.pubkey))} size={32} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function LiveKitUser({ p }: { p: RemoteParticipant | LocalParticipant }) {
|
function LiveKitUser({ p }: { p: RemoteParticipant | LocalParticipant }) {
|
||||||
const pubkey = p.identity.startsWith("guest-") ? "anon" : p.identity
|
const pubkey = p.identity.startsWith("guest-") ? "anon" : p.identity;
|
||||||
const profile = useUserProfile(pubkey);
|
const profile = useUserProfile(pubkey);
|
||||||
return <div className="flex flex-col gap-2 items-center text-center">
|
return (
|
||||||
|
<div className="flex flex-col gap-2 items-center text-center">
|
||||||
<Avatar pubkey={pubkey} className={p.isSpeaking ? "outline" : ""} user={profile} size={48} />
|
<Avatar pubkey={pubkey} className={p.isSpeaking ? "outline" : ""} user={profile} size={48} />
|
||||||
<DisplayName pubkey={pubkey} user={pubkey === "anon" ? { name: "Anon" } : profile} />
|
<DisplayName pubkey={pubkey} user={pubkey === "anon" ? { name: "Anon" } : profile} />
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
@ -3,10 +3,17 @@ import React from "react";
|
|||||||
|
|
||||||
import ProfileImage from "@/Components/User/ProfileImage";
|
import ProfileImage from "@/Components/User/ProfileImage";
|
||||||
|
|
||||||
export function AvatarGroup({ ids, onClick, size }: { ids: HexKey[]; onClick?: () => void, size?: number }) {
|
export function AvatarGroup({ ids, onClick, size }: { ids: HexKey[]; onClick?: () => void; size?: number }) {
|
||||||
return ids.map((a, index) => (
|
return ids.map((a, index) => (
|
||||||
<div className={`inline-block ${index > 0 ? "-ml-4" : ""}`} key={a} style={{ zIndex: ids.length - index }}>
|
<div className={`inline-block ${index > 0 ? "-ml-4" : ""}`} key={a} style={{ zIndex: ids.length - index }}>
|
||||||
<ProfileImage link="" onClick={onClick} showFollowDistance={false} pubkey={a} size={size ?? 24} showUsername={false} />
|
<ProfileImage
|
||||||
|
link=""
|
||||||
|
onClick={onClick}
|
||||||
|
showFollowDistance={false}
|
||||||
|
pubkey={a}
|
||||||
|
size={size ?? 24}
|
||||||
|
showUsername={false}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ export function extractStreamInfo(ev?: TaggedNostrEvent) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function sortStreamTags(tags: Array<string | Array<string>>) {
|
export function sortStreamTags(tags: Array<string | Array<string>>) {
|
||||||
const plainTags = tags.filter(a => (Array.isArray(a) ? a[0] === "t" : true)).map(a => (Array.isArray(a) ? a[1] : a));
|
const plainTags = tags.filter(a => (Array.isArray(a) ? a[0] === "t" : true)).map(a => (Array.isArray(a) ? a[1] : a));
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user