refactor: useRequestBuilder
This commit is contained in:
parent
9eb97701b4
commit
e877625923
@ -1,54 +0,0 @@
|
||||
.stream-list {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.stream-list::-webkit-scrollbar {
|
||||
height: 6.25px;
|
||||
}
|
||||
|
||||
.stream-event {
|
||||
display: flex;
|
||||
padding: 8px 12px;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.stream-event > div:first-of-type {
|
||||
border-radius: 8px;
|
||||
height: 49px;
|
||||
width: 65px;
|
||||
background-color: var(--gray-light);
|
||||
background-image: var(--img);
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.stream-event span.live {
|
||||
display: flex;
|
||||
padding: 4px 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 9px;
|
||||
background: var(--live);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.stream-event .details .reactions {
|
||||
color: var(--font-secondary-color);
|
||||
}
|
||||
|
||||
.stream-event .details > div:nth-of-type(2) {
|
||||
width: 100px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
@ -1,34 +1,51 @@
|
||||
import "./LiveStreams.css";
|
||||
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { EventKind, NostrEvent, NostrLink, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { useRequestBuilder, useUserProfile } from "@snort/system-react";
|
||||
import { CSSProperties, useMemo } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import useFollowsControls from "@/Hooks/useFollowControls";
|
||||
import useImgProxy from "@/Hooks/useImgProxy";
|
||||
import { findTag } from "@/Utils";
|
||||
import { Hour } from "@/Utils/Const";
|
||||
|
||||
import Avatar from "../User/Avatar";
|
||||
|
||||
export function LiveStreams() {
|
||||
const { followList } = useFollowsControls();
|
||||
const sub = useMemo(() => {
|
||||
const since = unixNow() - 60 * 60 * 24;
|
||||
const rb = new RequestBuilder("follows:streams");
|
||||
rb.withFilter().kinds([EventKind.LiveEvent]).authors(followList).since(since);
|
||||
rb.withFilter().kinds([EventKind.LiveEvent]).tag("p", followList).since(since);
|
||||
if (followList.length > 0) {
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.authors(followList)
|
||||
.since(unixNow() - Hour);
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.tag("p", followList)
|
||||
.since(unixNow() - Hour);
|
||||
}
|
||||
return rb;
|
||||
}, [followList]);
|
||||
}, [followList.length]);
|
||||
|
||||
const streams = useRequestBuilder(sub);
|
||||
if (streams.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="stream-list">
|
||||
{streams.map(v => (
|
||||
<LiveStreamEvent ev={v} key={`${v.kind}:${v.pubkey}:${findTag(v, "d")}`} />
|
||||
))}
|
||||
<div className="flex mx-2 gap-4 overflow-x-auto sm-hide-scrollbar">
|
||||
{streams
|
||||
.filter(a => {
|
||||
return findTag(a, "status") === "live";
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const sA = Number(findTag(a, "starts"));
|
||||
const sB = Number(findTag(b, "starts"));
|
||||
return sA > sB ? -1 : 1;
|
||||
})
|
||||
.map(v => (
|
||||
<LiveStreamEvent ev={v} key={`${v.kind}:${v.pubkey}:${findTag(v, "d")}`} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -38,27 +55,33 @@ function LiveStreamEvent({ ev }: { ev: NostrEvent }) {
|
||||
const title = findTag(ev, "title");
|
||||
const image = findTag(ev, "image");
|
||||
const status = findTag(ev, "status");
|
||||
const viewers = findTag(ev, "current_participants");
|
||||
const host = ev.tags.find(a => a[0] === "p" && a[3] === "host")?.[1] ?? ev.pubkey;
|
||||
const hostProfile = useUserProfile(host);
|
||||
|
||||
const link = NostrLink.fromEvent(ev).encode(CONFIG.eventLinkPrefix);
|
||||
const link = NostrLink.fromEvent(ev).encode();
|
||||
const imageProxy = proxy(image ?? "");
|
||||
|
||||
return (
|
||||
<Link className="stream-event" to={`https://zap.stream/${link}`} target="_blank">
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--img": `url(${imageProxy})`,
|
||||
} as CSSProperties
|
||||
}></div>
|
||||
<div className="flex flex-col details">
|
||||
<div className="flex g2">
|
||||
<span className="live">{status}</span>
|
||||
<div className="reaction-pill">
|
||||
<Icon name="zap" size={24} />
|
||||
<div className="reaction-pill-number">0</div>
|
||||
</div>
|
||||
<Link className="flex gap-2 h-[80px]" to={`https://zap.stream/${link}`} target="_blank">
|
||||
<div className="relative aspect-video">
|
||||
<div
|
||||
className="absolute h-full w-full bg-center bg-cover bg-gray-ultradark rounded-lg"
|
||||
style={
|
||||
{
|
||||
backgroundImage: `url(${imageProxy})`,
|
||||
} as CSSProperties
|
||||
}></div>
|
||||
<div className="absolute left-0 top-7 w-full overflow-hidden">
|
||||
<div className="whitespace-nowrap px-2 text-ellipsis overflow-hidden text-xs">{title}</div>
|
||||
</div>
|
||||
<div className="absolute top-1 left-1 bg-heart rounded-md px-2 uppercase font-bold">{status}</div>
|
||||
<div className="absolute right-1 top-1">
|
||||
<Avatar pubkey={host} user={hostProfile} size={25} className="outline outline-2 outline-highlight" />
|
||||
</div>
|
||||
<div className="absolute left-1 bottom-1 rounded-md px-2 py-1 text-xs bg-gray font-medium">
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n: viewers }} />
|
||||
</div>
|
||||
<div>{title}</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
@ -11,14 +11,14 @@ import UptimeLabel from "./uptime-label";
|
||||
export default function RelayUptime({ url }: { url: string }) {
|
||||
const sub = useMemo(() => {
|
||||
const u = sanitizeRelayUrl(url);
|
||||
if (!u) return;
|
||||
|
||||
const rb = new RequestBuilder(`uptime`);
|
||||
rb.withFilter()
|
||||
.kinds([30_166 as EventKind])
|
||||
.tag("d", [u])
|
||||
.since(unixNow() - Day)
|
||||
.relay(MonitorRelays);
|
||||
if (u) {
|
||||
rb.withFilter()
|
||||
.kinds([30_166 as EventKind])
|
||||
.tag("d", [u])
|
||||
.since(unixNow() - Day)
|
||||
.relay(MonitorRelays);
|
||||
}
|
||||
return rb;
|
||||
}, [url]);
|
||||
|
||||
|
@ -2,18 +2,18 @@ import { EventKind, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useFollowsControls from "@/Hooks/useFollowControls";
|
||||
|
||||
export function useArticles() {
|
||||
const { publicKey, follows } = useLogin(s => ({ publicKey: s.publicKey, follows: s.state.follows }));
|
||||
const { followList } = useFollowsControls();
|
||||
|
||||
const sub = useMemo(() => {
|
||||
if (!publicKey) return null;
|
||||
const rb = new RequestBuilder(`articles:${publicKey}`);
|
||||
rb.withFilter().kinds([EventKind.LongFormTextNote]).authors(follows).limit(20);
|
||||
|
||||
const rb = new RequestBuilder("articles");
|
||||
if (followList.length > 0) {
|
||||
rb.withFilter().kinds([EventKind.LongFormTextNote]).authors(followList).limit(20);
|
||||
}
|
||||
return rb;
|
||||
}, [follows, publicKey]);
|
||||
}, [followList]);
|
||||
|
||||
return useRequestBuilder(sub);
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ type BadgeAwards = {
|
||||
|
||||
export default function useProfileBadges(pubkey?: HexKey) {
|
||||
const sub = useMemo(() => {
|
||||
if (!pubkey) return null;
|
||||
const b = new RequestBuilder(`badges:${pubkey.slice(0, 12)}`);
|
||||
b.withFilter().kinds([EventKind.ProfileBadges]).tag("d", ["profile_badges"]).authors([pubkey]);
|
||||
const b = new RequestBuilder("badges");
|
||||
if (pubkey) {
|
||||
b.withFilter().kinds([EventKind.ProfileBadges]).tag("d", ["profile_badges"]).authors([pubkey]);
|
||||
}
|
||||
return b;
|
||||
}, [pubkey]);
|
||||
|
||||
@ -47,10 +48,11 @@ export default function useProfileBadges(pubkey?: HexKey) {
|
||||
|
||||
const awardsSub = useMemo(() => {
|
||||
const ids = Object.keys(profile);
|
||||
if (!pubkey || ids.length === 0) return null;
|
||||
const b = new RequestBuilder(`profile_awards:${pubkey.slice(0, 12)}`);
|
||||
b.withFilter().kinds([EventKind.BadgeAward]).ids(ids);
|
||||
b.withFilter().kinds([EventKind.Badge]).tag("d", ds).authors(pubkeys);
|
||||
const b = new RequestBuilder(`profile_awards`);
|
||||
if (pubkey && ids.length > 0) {
|
||||
b.withFilter().kinds([EventKind.BadgeAward]).ids(ids);
|
||||
b.withFilter().kinds([EventKind.Badge]).tag("d", ds).authors(pubkeys);
|
||||
}
|
||||
return b;
|
||||
}, [profile, ds]);
|
||||
|
||||
|
@ -4,9 +4,10 @@ import { useMemo } from "react";
|
||||
|
||||
export default function useFollowersFeed(pubkey?: HexKey) {
|
||||
const sub = useMemo(() => {
|
||||
if (!pubkey) return null;
|
||||
const b = new RequestBuilder(`followers:${pubkey.slice(0, 12)}`);
|
||||
b.withFilter().kinds([EventKind.ContactList]).tag("p", [pubkey]);
|
||||
const b = new RequestBuilder(`followers`);
|
||||
if (pubkey) {
|
||||
b.withFilter().kinds([EventKind.ContactList]).tag("p", [pubkey]);
|
||||
}
|
||||
return b;
|
||||
}, [pubkey]);
|
||||
|
||||
|
@ -2,27 +2,19 @@ import { EventKind, HexKey, RequestBuilder, TaggedNostrEvent } from "@snort/syst
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
|
||||
export default function useFollowsFeed(pubkey?: HexKey) {
|
||||
const { publicKey, follows } = useLogin(s => ({ publicKey: s.publicKey, follows: s.state.follows }));
|
||||
const isMe = publicKey === pubkey;
|
||||
|
||||
const sub = useMemo(() => {
|
||||
if (isMe || !pubkey) return null;
|
||||
const b = new RequestBuilder(`follows:${pubkey.slice(0, 12)}`);
|
||||
b.withFilter().kinds([EventKind.ContactList]).authors([pubkey]);
|
||||
const b = new RequestBuilder(`follows:for`);
|
||||
if (pubkey) {
|
||||
b.withFilter().kinds([EventKind.ContactList]).authors([pubkey]);
|
||||
}
|
||||
return b;
|
||||
}, [isMe, pubkey]);
|
||||
}, [pubkey]);
|
||||
|
||||
const contactFeed = useRequestBuilder(sub);
|
||||
return useMemo(() => {
|
||||
if (isMe) {
|
||||
return follows;
|
||||
}
|
||||
|
||||
return getFollowing(contactFeed ?? [], pubkey);
|
||||
}, [isMe, contactFeed, follows, pubkey]);
|
||||
}, [contactFeed, pubkey]);
|
||||
}
|
||||
|
||||
export function getFollowing(notes: readonly TaggedNostrEvent[], pubkey?: HexKey) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { unixNow } from "@snort/shared";
|
||||
import { removeUndefined, unixNow } from "@snort/shared";
|
||||
import { EventKind, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { useMemo } from "react";
|
||||
@ -7,12 +7,12 @@ import useLogin from "@/Hooks/useLogin";
|
||||
import { Hour } from "@/Utils/Const";
|
||||
|
||||
export default function useHashtagsFeed() {
|
||||
const { hashtags } = useLogin(s => ({ hashtags: s.tags.item }));
|
||||
const { hashtags } = useLogin(s => ({ hashtags: s.state.getList(EventKind.InterestsList) }));
|
||||
const sub = useMemo(() => {
|
||||
const rb = new RequestBuilder("hashtags-feed");
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.TextNote, EventKind.LiveEvent, EventKind.LongFormTextNote, EventKind.Polls])
|
||||
.tag("t", hashtags)
|
||||
.tag("t", removeUndefined(hashtags.map(a => a.toEventTag()?.[1])))
|
||||
.since(unixNow() - Hour * 6);
|
||||
return rb;
|
||||
}, [hashtags]);
|
||||
|
@ -28,19 +28,19 @@ export default function useLoginFeed() {
|
||||
}, [login, publisher, system]);
|
||||
|
||||
const subLogin = useMemo(() => {
|
||||
if (!login || !pubKey) return null;
|
||||
|
||||
if (CONFIG.features.subscriptions && !login.readonly) {
|
||||
const b = new RequestBuilder(`login:${pubKey.slice(0, 12)}`);
|
||||
const b = new RequestBuilder(`login`);
|
||||
b.withOptions({
|
||||
leaveOpen: true,
|
||||
});
|
||||
b.withFilter()
|
||||
.relay("wss://relay.snort.social/")
|
||||
.kinds([EventKind.SnortSubscriptions])
|
||||
.authors([bech32ToHex(SnortPubKey)])
|
||||
.tag("p", [pubKey])
|
||||
.limit(10);
|
||||
if (pubKey) {
|
||||
b.withFilter()
|
||||
.relay("wss://relay.snort.social/")
|
||||
.kinds([EventKind.SnortSubscriptions])
|
||||
.authors([bech32ToHex(SnortPubKey)])
|
||||
.tag("p", [pubKey])
|
||||
.limit(10);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}, [pubKey, login]);
|
||||
|
@ -4,9 +4,10 @@ import { useMemo } from "react";
|
||||
|
||||
export default function useRelaysFeed(pubkey?: HexKey) {
|
||||
const sub = useMemo(() => {
|
||||
if (!pubkey) return null;
|
||||
const b = new RequestBuilder(`relays:${pubkey.slice(0, 12)}`);
|
||||
b.withFilter().authors([pubkey]).kinds([EventKind.Relays]);
|
||||
const b = new RequestBuilder(`relays:${pubkey ?? ""}`);
|
||||
if (pubkey) {
|
||||
b.withFilter().authors([pubkey]).kinds([EventKind.Relays]);
|
||||
}
|
||||
return b;
|
||||
}, [pubkey]);
|
||||
|
||||
|
@ -7,14 +7,13 @@ import { findTag } from "@/Utils";
|
||||
|
||||
export function useStatusFeed(id?: string, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
if (!id) return null;
|
||||
|
||||
const rb = new RequestBuilder(`statud:${id}`);
|
||||
rb.withOptions({ leaveOpen });
|
||||
rb.withFilter()
|
||||
.kinds([30315 as EventKind])
|
||||
.authors([id]);
|
||||
|
||||
if (id) {
|
||||
rb.withFilter()
|
||||
.kinds([30315 as EventKind])
|
||||
.authors([id]);
|
||||
}
|
||||
return rb;
|
||||
}, [id]);
|
||||
|
||||
|
@ -34,10 +34,6 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
const { isEventMuted } = useModeration();
|
||||
|
||||
const createBuilder = useCallback(() => {
|
||||
if (subject.type !== "global" && subject.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const kinds =
|
||||
subject.kinds ??
|
||||
(subject.type === "profile_keyword"
|
||||
@ -80,19 +76,17 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
|
||||
const sub = useMemo(() => {
|
||||
const rb = createBuilder();
|
||||
if (rb) {
|
||||
for (const filter of rb.filterBuilders) {
|
||||
if (options.method === "LIMIT_UNTIL") {
|
||||
filter.until(until).limit(50);
|
||||
} else {
|
||||
filter.since(since).until(until);
|
||||
if (since === undefined) {
|
||||
filter.limit(50);
|
||||
}
|
||||
for (const filter of rb.filterBuilders) {
|
||||
if (options.method === "LIMIT_UNTIL") {
|
||||
filter.until(until).limit(50);
|
||||
} else {
|
||||
filter.since(since).until(until);
|
||||
if (since === undefined) {
|
||||
filter.limit(50);
|
||||
}
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
return rb;
|
||||
}, [until, since, options.method, createBuilder]);
|
||||
|
||||
const mainQuery = useRequestBuilderAdvanced(sub);
|
||||
@ -108,7 +102,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
|
||||
const subRealtime = useMemo(() => {
|
||||
const rb = createBuilder();
|
||||
if (rb && !autoShowLatest && options.method !== "LIMIT_UNTIL") {
|
||||
if (!autoShowLatest && options.method !== "LIMIT_UNTIL") {
|
||||
rb.withOptions({
|
||||
leaveOpen: true,
|
||||
});
|
||||
@ -116,8 +110,11 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
for (const filter of rb.filterBuilders) {
|
||||
filter.limit(1).since(now);
|
||||
}
|
||||
return rb;
|
||||
} else {
|
||||
rb.clear();
|
||||
}
|
||||
|
||||
return rb;
|
||||
}, [autoShowLatest, createBuilder]);
|
||||
|
||||
const latestQuery = useRequestBuilderAdvanced(subRealtime);
|
||||
|
@ -4,9 +4,10 @@ import { useMemo } from "react";
|
||||
|
||||
export default function useZapsFeed(link?: NostrLink) {
|
||||
const sub = useMemo(() => {
|
||||
if (!link) return null;
|
||||
const b = new RequestBuilder(`zaps:${link.encode()}`);
|
||||
b.withFilter().kinds([EventKind.ZapReceipt]).replyToLink([link]);
|
||||
const b = new RequestBuilder(`zaps:${link?.encode()}`);
|
||||
if (link) {
|
||||
b.withFilter().kinds([EventKind.ZapReceipt]).replyToLink([link]);
|
||||
}
|
||||
return b;
|
||||
}, [link]);
|
||||
|
||||
|
@ -7,9 +7,7 @@ import { createEmptyChatObject } from "@/chat";
|
||||
|
||||
export function useEmptyChatSystem(id?: string) {
|
||||
const sub = useMemo(() => {
|
||||
if (!id) return;
|
||||
|
||||
if (id.startsWith(NostrPrefix.Chat28)) {
|
||||
if (id?.startsWith(NostrPrefix.Chat28)) {
|
||||
const cx = unwrap(decodeTLV(id).find(a => a.type === TLVEntryType.Special)).value as string;
|
||||
const rb = new RequestBuilder(`nip28:${id}`);
|
||||
rb.withFilter().ids([cx]).kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]);
|
||||
@ -18,6 +16,8 @@ export function useEmptyChatSystem(id?: string) {
|
||||
.kinds([EventKind.PublicChatChannel, EventKind.PublicChatMessage, EventKind.PublicChatMetadata]);
|
||||
|
||||
return rb;
|
||||
} else {
|
||||
return new RequestBuilder(id ?? "");
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
|
@ -9,9 +9,7 @@ export function useLinkList(id: string, fn: (rb: RequestBuilder) => void) {
|
||||
const sub = useMemo(() => {
|
||||
const rb = new RequestBuilder(id);
|
||||
fn(rb);
|
||||
if (rb.numFilters > 0) {
|
||||
return rb;
|
||||
}
|
||||
return rb;
|
||||
}, [id, fn]);
|
||||
|
||||
const listStore = useRequestBuilder(sub);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { lazy } from "react";
|
||||
import { Outlet, RouteObject } from "react-router-dom";
|
||||
|
||||
import { LiveStreams } from "@/Components/LiveStream/LiveStreams";
|
||||
import { RootTabRoutes } from "@/Pages/Root/RootTabRoutes";
|
||||
import { getCurrentRefCode } from "@/Utils";
|
||||
|
||||
@ -9,6 +10,7 @@ export default function RootPage() {
|
||||
const code = getCurrentRefCode();
|
||||
return (
|
||||
<>
|
||||
<LiveStreams />
|
||||
<div className="main-content">
|
||||
<Outlet />
|
||||
</div>
|
||||
|
@ -63,7 +63,7 @@ export interface ChatSystem {
|
||||
/**
|
||||
* Create a request for this system to get updates
|
||||
*/
|
||||
subscription(session: LoginSession): RequestBuilder | undefined;
|
||||
subscription(session: LoginSession): RequestBuilder;
|
||||
|
||||
/**
|
||||
* Create a list of chats for a given pubkey and set of events
|
||||
|
@ -31,10 +31,11 @@ export class Nip17ChatSystem extends ExternalStore<Array<Chat>> implements ChatS
|
||||
|
||||
subscription(session: LoginSession) {
|
||||
const pk = session.publicKey;
|
||||
if (!pk || session.readonly) return;
|
||||
const rb = new RequestBuilder(`nip17:${pk?.slice(0, 12)}`);
|
||||
|
||||
const rb = new RequestBuilder(`nip17:${pk.slice(0, 12)}`);
|
||||
rb.withFilter().kinds([EventKind.GiftWrap]).tag("p", [pk]);
|
||||
if (pk && !session.readonly) {
|
||||
rb.withFilter().kinds([EventKind.GiftWrap]).tag("p", [pk]);
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
|
||||
|
@ -25,21 +25,20 @@ export class Nip28ChatSystem implements ChatSystem {
|
||||
EventKind.PublicChatMuteUser,
|
||||
];
|
||||
|
||||
subscription(session: LoginSession): RequestBuilder | undefined {
|
||||
subscription(session: LoginSession): RequestBuilder {
|
||||
const chats = (session.extraChats ?? []).filter(a => a.startsWith(NostrPrefix.Chat28));
|
||||
if (chats.length === 0) return;
|
||||
|
||||
const chatId = (v: string) => unwrap(decodeTLV(v).find(a => a.type === TLVEntryType.Special)).value as string;
|
||||
|
||||
const rb = new RequestBuilder(`nip28:${session.id}`);
|
||||
rb.withFilter()
|
||||
.ids(chats.map(v => chatId(v)))
|
||||
.kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]);
|
||||
for (const c of chats) {
|
||||
const id = chatId(c);
|
||||
rb.withFilter().tag("e", [id]).kinds(this.ChannelKinds);
|
||||
if (chats.length > 0) {
|
||||
rb.withFilter()
|
||||
.ids(chats.map(v => chatId(v)))
|
||||
.kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]);
|
||||
for (const c of chats) {
|
||||
const id = chatId(c);
|
||||
rb.withFilter().tag("e", [id]).kinds(this.ChannelKinds);
|
||||
}
|
||||
}
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@ -940,6 +941,15 @@ svg.repeat {
|
||||
.pb-safe-area-plus-footer {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 56px);
|
||||
}
|
||||
.sm-hide-scrollbar {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.sm-hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
/* Safari and Chrome */
|
||||
}
|
||||
}
|
||||
|
||||
.active > .icon-outline {
|
||||
|
@ -189,6 +189,9 @@
|
||||
"3QwfJR": {
|
||||
"defaultMessage": "~{amount}"
|
||||
},
|
||||
"3adEeb": {
|
||||
"defaultMessage": "{n} viewers"
|
||||
},
|
||||
"3cc4Ct": {
|
||||
"defaultMessage": "Light"
|
||||
},
|
||||
|
@ -62,6 +62,7 @@
|
||||
"3GWu6/": "User Statuses",
|
||||
"3KNMbJ": "Articles",
|
||||
"3QwfJR": "~{amount}",
|
||||
"3adEeb": "{n} viewers",
|
||||
"3cc4Ct": "Light",
|
||||
"3gOsZq": "Translators",
|
||||
"3kbIhS": "Untitled",
|
||||
|
@ -40,6 +40,15 @@ module.exports = {
|
||||
background: "var(--bg-color)",
|
||||
secondary: "var(--bg-secondary)",
|
||||
},
|
||||
animation: {
|
||||
"infinite-scroll": "infinite-scroll 25s linear infinite",
|
||||
},
|
||||
keyframes: {
|
||||
"infinite-scroll": {
|
||||
from: { transform: "translateX(0)" },
|
||||
to: { transform: "translateX(-100%)" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
@ -30,7 +30,7 @@ export function useReactions(
|
||||
}
|
||||
}
|
||||
others?.(rb);
|
||||
return rb.numFilters > 0 ? rb : undefined;
|
||||
return rb;
|
||||
}, [ids]);
|
||||
|
||||
return useRequestBuilder(sub);
|
||||
|
@ -5,25 +5,20 @@ import { SnortContext } from "./context";
|
||||
/**
|
||||
* Send a query to the relays and wait for data
|
||||
*/
|
||||
export function useRequestBuilder(rb: RequestBuilder | null | undefined) {
|
||||
export function useRequestBuilder(rb: RequestBuilder) {
|
||||
const system = useContext(SnortContext);
|
||||
return useSyncExternalStore(
|
||||
v => {
|
||||
if (rb) {
|
||||
const q = system.Query(rb);
|
||||
q.on("event", v);
|
||||
q.uncancel();
|
||||
return () => {
|
||||
q.off("event", v);
|
||||
q.cancel();
|
||||
};
|
||||
}
|
||||
const q = system.Query(rb);
|
||||
q.on("event", v);
|
||||
q.uncancel();
|
||||
return () => {
|
||||
// noop
|
||||
q.off("event", v);
|
||||
q.cancel();
|
||||
};
|
||||
},
|
||||
() => {
|
||||
const q = system.GetQuery(rb?.id ?? "");
|
||||
const q = system.GetQuery(rb.id);
|
||||
if (q) {
|
||||
return q.snapshot;
|
||||
} else {
|
||||
@ -36,14 +31,12 @@ export function useRequestBuilder(rb: RequestBuilder | null | undefined) {
|
||||
/**
|
||||
* More advanced hook which returns the Query object
|
||||
*/
|
||||
export function useRequestBuilderAdvanced(rb: RequestBuilder | null | undefined) {
|
||||
export function useRequestBuilderAdvanced(rb: RequestBuilder) {
|
||||
const system = useContext(SnortContext);
|
||||
const q = useMemo(() => {
|
||||
if (rb) {
|
||||
const q = system.Query(rb);
|
||||
q.uncancel();
|
||||
return q;
|
||||
}
|
||||
const q = system.Query(rb);
|
||||
q.uncancel();
|
||||
return q;
|
||||
}, [rb]);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -56,7 +56,9 @@ export class QueryManager extends EventEmitter<QueryManagerEvents> {
|
||||
});
|
||||
|
||||
this.#queries.set(req.id, q);
|
||||
this.emit("change");
|
||||
if (req.numFilters > 0) {
|
||||
this.emit("change");
|
||||
}
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
@ -181,10 +181,13 @@ export class Query extends EventEmitter<QueryEvents> {
|
||||
* Adds another request to this one
|
||||
*/
|
||||
addRequest(req: RequestBuilder) {
|
||||
this.#log("Add query %O to %s", req, this.id);
|
||||
this.requests.push(...req.buildRaw());
|
||||
this.#start();
|
||||
return true;
|
||||
if (req.numFilters > 0) {
|
||||
this.#log("Add query %O to %s", req, this.id);
|
||||
this.requests.push(...req.buildRaw());
|
||||
this.#start();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isOpen() {
|
||||
@ -326,7 +329,9 @@ export class Query extends EventEmitter<QueryEvents> {
|
||||
if (this.#replaceable) {
|
||||
rawFilters.push(...this.filters);
|
||||
}
|
||||
this.emit("request", this.id, rawFilters);
|
||||
if (rawFilters.length > 0) {
|
||||
this.emit("request", this.id, rawFilters);
|
||||
}
|
||||
}
|
||||
|
||||
#stopCheckTraces() {
|
||||
|
@ -72,6 +72,11 @@ export class RequestBuilder {
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#builders = [];
|
||||
this.#options = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another request builders filters to this one
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user