feat: pubkey stream loading
chore: faster stream navigation
This commit is contained in:
@ -31,7 +31,9 @@ function NewStream({ ev, onFinish }: StreamEditorProps) {
|
|||||||
onFinish={(ex) => {
|
onFinish={(ex) => {
|
||||||
currentProvider.updateStreamInfo(ex);
|
currentProvider.updateStreamInfo(ex);
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
navigate(eventLink(ex));
|
navigate(`/${eventLink(ex)}`, {
|
||||||
|
state: ev
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
onFinish?.(ev);
|
onFinish?.(ev);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ export function VideoTile({
|
|||||||
to={`/${link}`}
|
to={`/${link}`}
|
||||||
className={`video-tile${contentWarning ? " nsfw" : ""}`}
|
className={`video-tile${contentWarning ? " nsfw" : ""}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
state={ev}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
import { unwrap } from "@snort/shared";
|
import { unwrap } from "@snort/shared";
|
||||||
import {
|
import {
|
||||||
|
NostrEvent,
|
||||||
NostrLink,
|
NostrLink,
|
||||||
NostrPrefix,
|
NostrPrefix,
|
||||||
NoteCollection,
|
NoteCollection,
|
||||||
RequestBuilder,
|
RequestBuilder,
|
||||||
|
TaggedRawEvent,
|
||||||
} from "@snort/system";
|
} from "@snort/system";
|
||||||
import { useRequestBuilder } from "@snort/system-react";
|
import { useRequestBuilder } from "@snort/system-react";
|
||||||
import { LIVE_STREAM } from "const";
|
import { LIVE_STREAM } from "const";
|
||||||
import { System } from "index";
|
import { System } from "index";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false) {
|
export function useCurrentStreamFeed(
|
||||||
|
link: NostrLink,
|
||||||
|
leaveOpen = false,
|
||||||
|
evPreload?: NostrEvent
|
||||||
|
) {
|
||||||
const author =
|
const author =
|
||||||
link.type === NostrPrefix.Address ? unwrap(link.author) : link.id;
|
link.type === NostrPrefix.Address ? unwrap(link.author) : link.id;
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
@ -38,6 +44,10 @@ export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false) {
|
|||||||
|
|
||||||
const q = useRequestBuilder(System, NoteCollection, sub);
|
const q = useRequestBuilder(System, NoteCollection, sub);
|
||||||
|
|
||||||
|
if (evPreload) {
|
||||||
|
q.add(evPreload as TaggedRawEvent);
|
||||||
|
}
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const hosting = q.data?.filter(
|
const hosting = q.data?.filter(
|
||||||
(a) =>
|
(a) =>
|
||||||
|
@ -15,6 +15,7 @@ import { ChatPopout } from "pages/chat-popout";
|
|||||||
import { LoginStore } from "login";
|
import { LoginStore } from "login";
|
||||||
import { StreamProvidersPage } from "pages/providers";
|
import { StreamProvidersPage } from "pages/providers";
|
||||||
import { defaultRelays } from "const";
|
import { defaultRelays } from "const";
|
||||||
|
import { CatchAllRoutePage } from "pages/catch-all";
|
||||||
|
|
||||||
export enum StreamState {
|
export enum StreamState {
|
||||||
Live = "live",
|
Live = "live",
|
||||||
@ -58,6 +59,10 @@ const router = createBrowserRouter([
|
|||||||
path: "/providers/:id?",
|
path: "/providers/:id?",
|
||||||
element: <StreamProvidersPage />,
|
element: <StreamProvidersPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "*",
|
||||||
|
element: <CatchAllRoutePage />
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
6
src/pages/catch-all.tsx
Normal file
6
src/pages/catch-all.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export function CatchAllRoutePage() {
|
||||||
|
//const { ["*"]: param } = useParams();
|
||||||
|
|
||||||
|
return <b className="error">Not found :(</b>
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
import "./stream-page.css";
|
import "./stream-page.css";
|
||||||
import { parseNostrLink, TaggedRawEvent } from "@snort/system";
|
import { parseNostrLink, TaggedRawEvent } from "@snort/system";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
import { LiveVideoPlayer } from "element/live-video-player";
|
import { LiveVideoPlayer } from "element/live-video-player";
|
||||||
import { findTag, getHost } from "utils";
|
import { createNostrLink, findTag, getEventFromLocationState, getHost } from "utils";
|
||||||
import { Profile, getName } from "element/profile";
|
import { Profile, getName } from "element/profile";
|
||||||
import { LiveChat } from "element/live-chat";
|
import { LiveChat } from "element/live-chat";
|
||||||
import AsyncButton from "element/async-button";
|
import AsyncButton from "element/async-button";
|
||||||
import useEventFeed from "hooks/event-feed";
|
|
||||||
import { useLogin } from "hooks/login";
|
import { useLogin } from "hooks/login";
|
||||||
import { useZapGoal } from "hooks/goals";
|
import { useZapGoal } from "hooks/goals";
|
||||||
import { StreamState, System } from "index";
|
import { StreamState, System } from "index";
|
||||||
@ -26,6 +25,7 @@ import {
|
|||||||
ContentWarningOverlay,
|
ContentWarningOverlay,
|
||||||
isContentWarningAccepted,
|
isContentWarningAccepted,
|
||||||
} from "element/content-warning";
|
} from "element/content-warning";
|
||||||
|
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||||
|
|
||||||
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
@ -107,8 +107,10 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
|||||||
|
|
||||||
export function StreamPage() {
|
export function StreamPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const location = useLocation();
|
||||||
|
const evPreload = getEventFromLocationState(location.state);
|
||||||
const link = parseNostrLink(params.id!);
|
const link = parseNostrLink(params.id!);
|
||||||
const { data: ev } = useEventFeed(link, true);
|
const ev = useCurrentStreamFeed(link, true, evPreload);
|
||||||
const host = getHost(ev);
|
const host = getHost(ev);
|
||||||
const goal = useZapGoal(host, link, true);
|
const goal = useZapGoal(host, link, true);
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ export function StreamPage() {
|
|||||||
<ProfileInfo ev={ev} goal={goal} />
|
<ProfileInfo ev={ev} goal={goal} />
|
||||||
<StreamCards host={host} />
|
<StreamCards host={host} />
|
||||||
</div>
|
</div>
|
||||||
<LiveChat link={link} ev={ev} goal={goal} />
|
<LiveChat link={createNostrLink(ev) ?? link} ev={ev} goal={goal} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
38
src/utils.ts
38
src/utils.ts
@ -1,7 +1,14 @@
|
|||||||
import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
|
import {
|
||||||
|
NostrEvent,
|
||||||
|
NostrPrefix,
|
||||||
|
TaggedRawEvent,
|
||||||
|
encodeTLV,
|
||||||
|
parseNostrLink,
|
||||||
|
} from "@snort/system";
|
||||||
import * as utils from "@noble/curves/abstract/utils";
|
import * as utils from "@noble/curves/abstract/utils";
|
||||||
import { bech32 } from "@scure/base";
|
import { bech32 } from "@scure/base";
|
||||||
import type { Tag, Tags } from "types";
|
import type { Tag, Tags } from "types";
|
||||||
|
import { LIVE_STREAM } from "const";
|
||||||
|
|
||||||
export function toAddress(e: NostrEvent): string {
|
export function toAddress(e: NostrEvent): string {
|
||||||
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
||||||
@ -70,16 +77,28 @@ export function splitByUrl(str: string) {
|
|||||||
return str.split(urlRegex);
|
return str.split(urlRegex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function eventLink(ev: NostrEvent) {
|
export function eventLink(ev: NostrEvent | TaggedRawEvent) {
|
||||||
|
if (ev.kind && ev.kind >= 30000 && ev.kind <= 40000) {
|
||||||
const d = findTag(ev, "d") ?? "";
|
const d = findTag(ev, "d") ?? "";
|
||||||
const naddr = encodeTLV(
|
return encodeTLV(
|
||||||
NostrPrefix.Address,
|
NostrPrefix.Address,
|
||||||
d,
|
d,
|
||||||
undefined,
|
"relays" in ev ? ev.relays : undefined,
|
||||||
ev.kind,
|
ev.kind,
|
||||||
ev.pubkey
|
ev.pubkey
|
||||||
);
|
);
|
||||||
return `/${naddr}`;
|
} else {
|
||||||
|
return encodeTLV(
|
||||||
|
NostrPrefix.Event,
|
||||||
|
ev.id,
|
||||||
|
"relays" in ev ? ev.relays : undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNostrLink(ev?: NostrEvent) {
|
||||||
|
if (!ev) return;
|
||||||
|
return parseNostrLink(eventLink(ev));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHost(ev?: NostrEvent) {
|
export function getHost(ev?: NostrEvent) {
|
||||||
@ -113,3 +132,12 @@ export function getTagValues(tags: Tags, tag: string): Array<string> {
|
|||||||
.filter((t) => t)
|
.filter((t) => t)
|
||||||
.map((t) => t as string);
|
.map((t) => t as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getEventFromLocationState(state: unknown | undefined | null) {
|
||||||
|
return state &&
|
||||||
|
typeof state === "object" &&
|
||||||
|
"kind" in state &&
|
||||||
|
state.kind === LIVE_STREAM
|
||||||
|
? (state as NostrEvent)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user