chore: cleanup subs
This commit is contained in:
parent
a49b40d9b3
commit
3cbec9f272
@ -15,7 +15,7 @@ import LNURLTip from "Element/LNURLTip";
|
|||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
import useProfile from "Feed/ProfileFeed";
|
import useProfile from "Feed/ProfileFeed";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import { hexToBech32 } from "Util";
|
import { debounce, hexToBech32 } from "Util";
|
||||||
import { UserMetadata } from "Nostr";
|
import { UserMetadata } from "Nostr";
|
||||||
|
|
||||||
type Nip05ServiceProps = {
|
type Nip05ServiceProps = {
|
||||||
@ -77,7 +77,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
|
|||||||
setAvailabilityResponse({ available: false, why: "REGEX" });
|
setAvailabilityResponse({ available: false, why: "REGEX" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let t = setTimeout(() => {
|
return debounce(500, () => {
|
||||||
svc.CheckAvailable(handle, domain)
|
svc.CheckAvailable(handle, domain)
|
||||||
.then(a => {
|
.then(a => {
|
||||||
if ('error' in a) {
|
if ('error' in a) {
|
||||||
@ -87,8 +87,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}, 500);
|
});
|
||||||
return () => clearTimeout(t);
|
|
||||||
}
|
}
|
||||||
}, [handle, domain]);
|
}, [handle, domain]);
|
||||||
|
|
||||||
@ -182,7 +181,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
|
|||||||
show={showInvoice}
|
show={showInvoice}
|
||||||
onClose={() => setShowInvoice(false)}
|
onClose={() => setShowInvoice(false)}
|
||||||
title={`Buying ${handle}@${domain}`}
|
title={`Buying ${handle}@${domain}`}
|
||||||
notice="DO NOT CLOSE THIS POPUP OR YOUR ORDER WILL GET STUCK"/>
|
notice="DO NOT CLOSE THIS POPUP OR YOUR ORDER WILL GET STUCK" />
|
||||||
{registerStatus?.paid && <div className="flex f-col">
|
{registerStatus?.paid && <div className="flex f-col">
|
||||||
<h4>Order Paid!</h4>
|
<h4>Order Paid!</h4>
|
||||||
<p>Your new NIP-05 handle is: <code>{handle}@{domain}</code></p>
|
<p>Your new NIP-05 handle is: <code>{handle}@{domain}</code></p>
|
||||||
|
@ -63,7 +63,7 @@ export default function NoteReaction(props: NoteReactionProps) {
|
|||||||
const root = extractRoot();
|
const root = extractRoot();
|
||||||
const opt = {
|
const opt = {
|
||||||
showHeader: ev?.Kind === EventKind.Repost,
|
showHeader: ev?.Kind === EventKind.Repost,
|
||||||
showFooter: ev?.Kind === EventKind.Repost,
|
showFooter: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -19,7 +19,7 @@ export interface TimelineProps {
|
|||||||
* A list of notes by pubkeys
|
* A list of notes by pubkeys
|
||||||
*/
|
*/
|
||||||
export default function Timeline({ subject, postsOnly = false, method }: TimelineProps) {
|
export default function Timeline({ subject, postsOnly = false, method }: TimelineProps) {
|
||||||
const { main, related, latest, loadMore, showLatest } = useTimelineFeed(subject, {
|
const { main, related, latest, parent, loadMore, showLatest } = useTimelineFeed(subject, {
|
||||||
method
|
method
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,7 +42,8 @@ export default function Timeline({ subject, postsOnly = false, method }: Timelin
|
|||||||
}
|
}
|
||||||
case EventKind.Reaction:
|
case EventKind.Reaction:
|
||||||
case EventKind.Repost: {
|
case EventKind.Repost: {
|
||||||
return <NoteReaction data={e} key={e.id} />
|
let eRef = e.tags.find(a => a[0] === "e")?.at(1);
|
||||||
|
return <NoteReaction data={e} key={e.id} root={parent.notes.find(a => a.id === eRef)}/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { useEffect, useMemo, useReducer, useState } from "react";
|
|||||||
import { System } from "Nostr/System";
|
import { System } from "Nostr/System";
|
||||||
import { TaggedRawEvent } from "Nostr";
|
import { TaggedRawEvent } from "Nostr";
|
||||||
import { Subscriptions } from "Nostr/Subscriptions";
|
import { Subscriptions } from "Nostr/Subscriptions";
|
||||||
|
import { debounce } from "Util";
|
||||||
|
|
||||||
export type NoteStore = {
|
export type NoteStore = {
|
||||||
notes: Array<TaggedRawEvent>,
|
notes: Array<TaggedRawEvent>,
|
||||||
@ -60,6 +61,11 @@ export interface UseSubscriptionState {
|
|||||||
append: (notes: TaggedRawEvent[]) => void
|
append: (notes: TaggedRawEvent[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait time before returning changed state
|
||||||
|
*/
|
||||||
|
const DebounceMs = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Subscriptions} sub
|
* @param {Subscriptions} sub
|
||||||
@ -68,7 +74,16 @@ export interface UseSubscriptionState {
|
|||||||
*/
|
*/
|
||||||
export default function useSubscription(sub: Subscriptions | null, options?: UseSubscriptionOptions): UseSubscriptionState {
|
export default function useSubscription(sub: Subscriptions | null, options?: UseSubscriptionOptions): UseSubscriptionState {
|
||||||
const [state, dispatch] = useReducer(notesReducer, initStore);
|
const [state, dispatch] = useReducer(notesReducer, initStore);
|
||||||
const [debounce, setDebounce] = useState<number>(0);
|
const [debounceOutput, setDebounceOutput] = useState<number>(0);
|
||||||
|
const [subDebounce, setSubDebounced] = useState<Subscriptions>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sub) {
|
||||||
|
return debounce(DebounceMs, () => {
|
||||||
|
setSubDebounced(sub);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [sub]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sub) {
|
if (sub) {
|
||||||
@ -99,16 +114,14 @@ export default function useSubscription(sub: Subscriptions | null, options?: Use
|
|||||||
System.RemoveSubscription(sub.Id);
|
System.RemoveSubscription(sub.Id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [sub]);
|
}, [subDebounce]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let t = setTimeout(() => {
|
return debounce(DebounceMs, () => {
|
||||||
setDebounce(s => s += 1);
|
setDebounceOutput(s => s += 1);
|
||||||
}, 100);
|
});
|
||||||
return () => clearTimeout(t);
|
|
||||||
}, [state]);
|
}, [state]);
|
||||||
|
|
||||||
const stateDebounced = useMemo(() => state, [debounce]);
|
const stateDebounced = useMemo(() => state, [debounceOutput]);
|
||||||
return {
|
return {
|
||||||
store: stateDebounced,
|
store: stateDebounced,
|
||||||
clear: () => {
|
clear: () => {
|
||||||
|
@ -6,6 +6,7 @@ import useSubscription from "Feed/Subscription";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
import { UserPreferences } from "State/Login";
|
import { UserPreferences } from "State/Login";
|
||||||
|
import { debounce } from "Util";
|
||||||
|
|
||||||
export default function useThreadFeed(id: u256) {
|
export default function useThreadFeed(id: u256) {
|
||||||
const [trackingEvents, setTrackingEvent] = useState<u256[]>([id]);
|
const [trackingEvents, setTrackingEvent] = useState<u256[]>([id]);
|
||||||
@ -14,9 +15,8 @@ export default function useThreadFeed(id: u256) {
|
|||||||
function addId(id: u256[]) {
|
function addId(id: u256[]) {
|
||||||
setTrackingEvent((s) => {
|
setTrackingEvent((s) => {
|
||||||
let orig = new Set(s);
|
let orig = new Set(s);
|
||||||
let idsMissing = id.filter(a => !orig.has(a));
|
if (id.some(a => !orig.has(a))) {
|
||||||
if (idsMissing.length > 0) {
|
let tmp = new Set([...s, ...id]);
|
||||||
let tmp = new Set([...s, ...idsMissing]);
|
|
||||||
return Array.from(tmp);
|
return Array.from(tmp);
|
||||||
} else {
|
} else {
|
||||||
return s;
|
return s;
|
||||||
@ -41,14 +41,18 @@ export default function useThreadFeed(id: u256) {
|
|||||||
const main = useSubscription(sub, { leaveOpen: true });
|
const main = useSubscription(sub, { leaveOpen: true });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// debounce
|
if (main.store) {
|
||||||
let t = setTimeout(() => {
|
return debounce(200, () => {
|
||||||
let eTags = main.store.notes.map(a => a.tags.filter(b => b[0] === "e").map(b => b[1])).flat();
|
let mainNotes = main.store.notes.filter(a => a.kind === EventKind.TextNote);
|
||||||
let ids = main.store.notes.map(a => a.id);
|
|
||||||
let allEvents = new Set([...eTags, ...ids]);
|
let eTags = mainNotes
|
||||||
addId(Array.from(allEvents));
|
.filter(a => a.kind === EventKind.TextNote)
|
||||||
}, 200);
|
.map(a => a.tags.filter(b => b[0] === "e").map(b => b[1])).flat();
|
||||||
return () => clearTimeout(t);
|
let ids = mainNotes.map(a => a.id);
|
||||||
|
let allEvents = new Set([...eTags, ...ids]);
|
||||||
|
addId(Array.from(allEvents));
|
||||||
|
})
|
||||||
|
}
|
||||||
}, [main.store]);
|
}, [main.store]);
|
||||||
|
|
||||||
return main.store;
|
return main.store;
|
||||||
|
@ -23,6 +23,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
const [until, setUntil] = useState<number>(now);
|
const [until, setUntil] = useState<number>(now);
|
||||||
const [since, setSince] = useState<number>(now - window);
|
const [since, setSince] = useState<number>(now - window);
|
||||||
const [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
|
const [trackingEvents, setTrackingEvent] = useState<u256[]>([]);
|
||||||
|
const [trackingParentEvents, setTrackingParentEvents] = useState<u256[]>([]);
|
||||||
const pref = useSelector<RootState, UserPreferences>(s => s.login.preferences);
|
const pref = useSelector<RootState, UserPreferences>(s => s.login.preferences);
|
||||||
|
|
||||||
function createSub() {
|
function createSub() {
|
||||||
@ -94,18 +95,30 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
const latest = useSubscription(subRealtime, { leaveOpen: true });
|
const latest = useSubscription(subRealtime, { leaveOpen: true });
|
||||||
|
|
||||||
const subNext = useMemo(() => {
|
const subNext = useMemo(() => {
|
||||||
|
let sub: Subscriptions | undefined;
|
||||||
if (trackingEvents.length > 0 && pref.enableReactions) {
|
if (trackingEvents.length > 0 && pref.enableReactions) {
|
||||||
let sub = new Subscriptions();
|
sub = new Subscriptions();
|
||||||
sub.Id = `timeline-related:${subject.type}`;
|
sub.Id = `timeline-related:${subject.type}`;
|
||||||
sub.Kinds = new Set([EventKind.Reaction, EventKind.Deletion, EventKind.Repost]);
|
sub.Kinds = new Set([EventKind.Reaction, EventKind.Deletion]);
|
||||||
sub.ETags = new Set(trackingEvents);
|
sub.ETags = new Set(trackingEvents);
|
||||||
return sub;
|
|
||||||
}
|
}
|
||||||
return null;
|
return sub ?? null;
|
||||||
}, [trackingEvents]);
|
}, [trackingEvents]);
|
||||||
|
|
||||||
const others = useSubscription(subNext, { leaveOpen: true });
|
const others = useSubscription(subNext, { leaveOpen: true });
|
||||||
|
|
||||||
|
const subParents = useMemo(() => {
|
||||||
|
if (trackingParentEvents.length > 0) {
|
||||||
|
let parents = new Subscriptions();
|
||||||
|
parents.Id = `timeline-parent:${subject.type}`;
|
||||||
|
parents.Ids = new Set(trackingParentEvents);
|
||||||
|
return parents;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [trackingParentEvents]);
|
||||||
|
|
||||||
|
const parent = useSubscription(subParents);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (main.store.notes.length > 0) {
|
if (main.store.notes.length > 0) {
|
||||||
setTrackingEvent(s => {
|
setTrackingEvent(s => {
|
||||||
@ -113,6 +126,20 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
let temp = new Set([...s, ...ids]);
|
let temp = new Set([...s, ...ids]);
|
||||||
return Array.from(temp);
|
return Array.from(temp);
|
||||||
});
|
});
|
||||||
|
let reposts = main.store.notes
|
||||||
|
.filter(a => a.kind === EventKind.Repost && a.content === "")
|
||||||
|
.map(a => a.tags.find(b => b[0] === "e"))
|
||||||
|
.filter(a => a)
|
||||||
|
.map(a => a![1]);
|
||||||
|
if (reposts.length > 0) {
|
||||||
|
setTrackingParentEvents(s => {
|
||||||
|
if (reposts.some(a => !s.includes(a))) {
|
||||||
|
let temp = new Set([...s, ...reposts]);
|
||||||
|
return Array.from(temp);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [main.store]);
|
}, [main.store]);
|
||||||
|
|
||||||
@ -120,6 +147,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
main: main.store,
|
main: main.store,
|
||||||
related: others.store,
|
related: others.store,
|
||||||
latest: latest.store,
|
latest: latest.store,
|
||||||
|
parent: parent.store,
|
||||||
loadMore: () => {
|
loadMore: () => {
|
||||||
console.debug("Timeline load more!")
|
console.debug("Timeline load more!")
|
||||||
if (options.method === "LIMIT_UNTIL") {
|
if (options.method === "LIMIT_UNTIL") {
|
||||||
|
11
src/Util.ts
11
src/Util.ts
@ -146,3 +146,14 @@ export function extractLnAddress(lnurl: string) {
|
|||||||
export function unixNow() {
|
export function unixNow() {
|
||||||
return Math.floor(new Date().getTime() / 1000);
|
return Math.floor(new Date().getTime() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple debounce
|
||||||
|
* @param timeout Time until falling edge
|
||||||
|
* @param fn Callack to run on falling edge
|
||||||
|
* @returns Cancel timeout function
|
||||||
|
*/
|
||||||
|
export function debounce(timeout: number, fn: () => void) {
|
||||||
|
let t = setTimeout(fn, timeout);
|
||||||
|
return () => clearTimeout(t);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user