chore: Update translations

This commit is contained in:
2024-09-19 18:41:09 +00:00
parent 6bc882fd41
commit 153a76a855
4 changed files with 160 additions and 139 deletions

View File

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

View File

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

View File

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

View File

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