refactor: cache since, dont dedupe by host
This commit is contained in:
@ -11,9 +11,10 @@ import { useMemo } from "react";
|
|||||||
import { LIVE_STREAM_CHAT } from "const";
|
import { LIVE_STREAM_CHAT } from "const";
|
||||||
|
|
||||||
export function useLiveChatFeed(link: NostrLink, eZaps?: Array<string>) {
|
export function useLiveChatFeed(link: NostrLink, eZaps?: Array<string>) {
|
||||||
const since = useMemo(() =>
|
const since = useMemo(
|
||||||
unixNow() - (60 * 60 * 24 * 7), // 7-days of zaps
|
() => unixNow() - 60 * 60 * 24 * 7, // 7-days of zaps
|
||||||
[link.id]);
|
[link.id],
|
||||||
|
);
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
const rb = new RequestBuilder(`live:${link.id}:${link.author}`);
|
const rb = new RequestBuilder(`live:${link.id}:${link.author}`);
|
||||||
rb.withOptions({
|
rb.withOptions({
|
||||||
@ -57,7 +58,7 @@ export function useLiveChatFeed(link: NostrLink, eZaps?: Array<string>) {
|
|||||||
const reactionsSub = useRequestBuilder<FlatNoteStore>(
|
const reactionsSub = useRequestBuilder<FlatNoteStore>(
|
||||||
System,
|
System,
|
||||||
FlatNoteStore,
|
FlatNoteStore,
|
||||||
esub
|
esub,
|
||||||
);
|
);
|
||||||
|
|
||||||
const reactions = reactionsSub.data ?? [];
|
const reactions = reactionsSub.data ?? [];
|
||||||
|
@ -6,26 +6,22 @@ import { useRequestBuilder } from "@snort/system-react";
|
|||||||
import { unixNow } from "@snort/shared";
|
import { unixNow } from "@snort/shared";
|
||||||
import { LIVE_STREAM } from "const";
|
import { LIVE_STREAM } from "const";
|
||||||
import { System, StreamState } from "index";
|
import { System, StreamState } from "index";
|
||||||
import { findTag, dedupeByHost } from "utils";
|
import { findTag } from "utils";
|
||||||
|
|
||||||
export function useStreamsFeed(tag?: string) {
|
export function useStreamsFeed(tag?: string) {
|
||||||
|
const since = useMemo(() => unixNow() - 86400, [tag]);
|
||||||
const rb = useMemo(() => {
|
const rb = useMemo(() => {
|
||||||
const rb = new RequestBuilder(tag ? `streams:${tag}` : "streams");
|
const rb = new RequestBuilder(tag ? `streams:${tag}` : "streams");
|
||||||
rb.withOptions({
|
rb.withOptions({
|
||||||
leaveOpen: true,
|
leaveOpen: true,
|
||||||
});
|
});
|
||||||
if (tag) {
|
if (tag) {
|
||||||
rb.withFilter()
|
rb.withFilter().kinds([LIVE_STREAM]).tag("t", [tag]).since(since);
|
||||||
.kinds([LIVE_STREAM])
|
|
||||||
.tag("t", [tag])
|
|
||||||
.since(unixNow() - 86400);
|
|
||||||
} else {
|
} else {
|
||||||
rb.withFilter()
|
rb.withFilter().kinds([LIVE_STREAM]).since(since);
|
||||||
.kinds([LIVE_STREAM])
|
|
||||||
.since(unixNow() - 86400);
|
|
||||||
}
|
}
|
||||||
return rb;
|
return rb;
|
||||||
}, [tag]);
|
}, [tag, since]);
|
||||||
|
|
||||||
const feed = useRequestBuilder<NoteCollection>(System, NoteCollection, rb);
|
const feed = useRequestBuilder<NoteCollection>(System, NoteCollection, rb);
|
||||||
const feedSorted = useMemo(() => {
|
const feedSorted = useMemo(() => {
|
||||||
@ -45,19 +41,17 @@ export function useStreamsFeed(tag?: string) {
|
|||||||
return [];
|
return [];
|
||||||
}, [feed.data]);
|
}, [feed.data]);
|
||||||
|
|
||||||
const live = dedupeByHost(
|
const live = feedSorted.filter(
|
||||||
feedSorted.filter((a) => findTag(a, "status") === StreamState.Live),
|
(a) => findTag(a, "status") === StreamState.Live,
|
||||||
);
|
);
|
||||||
const planned = dedupeByHost(
|
const planned = feedSorted.filter(
|
||||||
feedSorted.filter((a) => findTag(a, "status") === StreamState.Planned),
|
(a) => findTag(a, "status") === StreamState.Planned,
|
||||||
);
|
|
||||||
const ended = dedupeByHost(
|
|
||||||
feedSorted.filter((a) => {
|
|
||||||
const hasEnded = findTag(a, "status") === StreamState.Ended;
|
|
||||||
const recording = findTag(a, "recording");
|
|
||||||
return hasEnded && recording?.length > 0;
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
const ended = feedSorted.filter((a) => {
|
||||||
|
const hasEnded = findTag(a, "status") === StreamState.Ended;
|
||||||
|
const recording = findTag(a, "recording");
|
||||||
|
return hasEnded && recording?.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
return { live, planned, ended };
|
return { live, planned, ended };
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,26 @@ import type { NostrEvent } from "@snort/system";
|
|||||||
|
|
||||||
import { VideoTile } from "element/video-tile";
|
import { VideoTile } from "element/video-tile";
|
||||||
import { useLogin } from "hooks/login";
|
import { useLogin } from "hooks/login";
|
||||||
import { getHost, getTagValues, dedupeByHost } from "utils";
|
import { getHost, getTagValues } from "utils";
|
||||||
import { useStreamsFeed } from "hooks/live-streams";
|
import { useStreamsFeed } from "hooks/live-streams";
|
||||||
|
|
||||||
export function RootPage() {
|
export function RootPage() {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
|
|
||||||
const { live, planned, ended } = useStreamsFeed();
|
const { live, planned, ended } = useStreamsFeed();
|
||||||
const mutedHosts = getTagValues(login?.muted.tags ?? [], "p");
|
const mutedHosts = new Set(getTagValues(login?.muted.tags ?? [], "p"));
|
||||||
const followsHost = (ev: NostrEvent) => {
|
const followsHost = (ev: NostrEvent) => {
|
||||||
return login?.follows.tags?.find((t) => t.at(1) === getHost(ev));
|
return login?.follows.tags?.find((t) => t.at(1) === getHost(ev));
|
||||||
};
|
};
|
||||||
const hashtags = getTagValues(login?.follows.tags ?? [], "t");
|
const hashtags = getTagValues(login?.follows.tags ?? [], "t");
|
||||||
const following = dedupeByHost(live.filter(followsHost));
|
const following = live.filter(followsHost);
|
||||||
const liveNow = dedupeByHost(live.filter((e) => !following.includes(e)));
|
const liveNow = live.filter((e) => !following.includes(e));
|
||||||
const hasFollowingLive = following.length > 0;
|
const hasFollowingLive = following.length > 0;
|
||||||
|
|
||||||
const plannedEvents = planned
|
const plannedEvents = planned
|
||||||
.filter((e) => !mutedHosts.includes(getHost(e)))
|
.filter((e) => !mutedHosts.has(getHost(e)))
|
||||||
.filter(followsHost);
|
.filter(followsHost);
|
||||||
|
const endedEvents = ended.filter((e) => !mutedHosts.has(getHost(e)));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="homepage">
|
<div className="homepage">
|
||||||
@ -35,7 +36,7 @@ export function RootPage() {
|
|||||||
{!hasFollowingLive && (
|
{!hasFollowingLive && (
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{live
|
{live
|
||||||
.filter((e) => !mutedHosts.includes(getHost(e)))
|
.filter((e) => !mutedHosts.has(getHost(e)))
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
<VideoTile ev={e} key={e.id} />
|
<VideoTile ev={e} key={e.id} />
|
||||||
))}
|
))}
|
||||||
@ -46,7 +47,7 @@ export function RootPage() {
|
|||||||
<h2 className="divider line one-line">#{t}</h2>
|
<h2 className="divider line one-line">#{t}</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{live
|
{live
|
||||||
.filter((e) => !mutedHosts.includes(getHost(e)))
|
.filter((e) => !mutedHosts.has(getHost(e)))
|
||||||
.filter((e) => {
|
.filter((e) => {
|
||||||
const evTags = getTagValues(e.tags, "t");
|
const evTags = getTagValues(e.tags, "t");
|
||||||
return evTags.includes(t);
|
return evTags.includes(t);
|
||||||
@ -62,7 +63,7 @@ export function RootPage() {
|
|||||||
<h2 className="divider line one-line">Live</h2>
|
<h2 className="divider line one-line">Live</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{liveNow
|
{liveNow
|
||||||
.filter((e) => !mutedHosts.includes(getHost(e)))
|
.filter((e) => !mutedHosts.has(getHost(e)))
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
<VideoTile ev={e} key={e.id} />
|
<VideoTile ev={e} key={e.id} />
|
||||||
))}
|
))}
|
||||||
@ -79,15 +80,13 @@ export function RootPage() {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{ended.length > 0 && (
|
{endedEvents.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 className="divider line one-line">Ended</h2>
|
<h2 className="divider line one-line">Ended</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{ended
|
{endedEvents.map((e) => (
|
||||||
.filter((e) => !mutedHosts.includes(getHost(e)))
|
<VideoTile ev={e} key={e.id} />
|
||||||
.map((e) => (
|
))}
|
||||||
<VideoTile ev={e} key={e.id} />
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
19
src/utils.ts
19
src/utils.ts
@ -94,22 +94,3 @@ export async function openFile(): Promise<File | undefined> {
|
|||||||
export function getTagValues(tags: Array<string[]>, tag: string) {
|
export function getTagValues(tags: Array<string[]>, tag: string) {
|
||||||
return tags.filter((t) => t.at(0) === tag).map((t) => t.at(1));
|
return tags.filter((t) => t.at(0) === tag).map((t) => t.at(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dedupeByHost(events: Array<NostrEvent>) {
|
|
||||||
const { result } = events.reduce(
|
|
||||||
({ result, seen }, ev) => {
|
|
||||||
const host = getHost(ev);
|
|
||||||
if (seen.has(host)) {
|
|
||||||
return { result, seen };
|
|
||||||
}
|
|
||||||
result.push(ev);
|
|
||||||
seen.add(host);
|
|
||||||
return { result, seen };
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: [],
|
|
||||||
seen: new Set(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user