bug: load more fix
This commit is contained in:
parent
465c59ea20
commit
2ccf593476
@ -5,7 +5,7 @@ import { useInView } from "react-intersection-observer";
|
||||
import { TaggedRawEvent, EventKind, u256 } from "@snort/nostr";
|
||||
|
||||
import Icon from "Icons/Icon";
|
||||
import { dedupeByPubkey, findTag, tagFilterOfTextRepost, unixNow } from "Util";
|
||||
import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "Util";
|
||||
import ProfileImage from "Element/ProfileImage";
|
||||
import useTimelineFeed, { TimelineFeed, TimelineSubject } from "Feed/TimelineFeed";
|
||||
import LoadMore from "Element/LoadMore";
|
||||
|
@ -121,8 +121,8 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
|
||||
useEffect(() => {
|
||||
// clear store if changing relays
|
||||
main.store.clear();
|
||||
latest.store.clear();
|
||||
main.clear();
|
||||
latest.clear();
|
||||
}, [options.relay]);
|
||||
|
||||
const subNext = useMemo(() => {
|
||||
@ -180,7 +180,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
main: main.data,
|
||||
related: related.data,
|
||||
latest: latest.data,
|
||||
loading: main.store.loading,
|
||||
loading: main.loading(),
|
||||
loadMore: () => {
|
||||
if (main.data) {
|
||||
console.debug("Timeline load more!");
|
||||
@ -194,8 +194,8 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
},
|
||||
showLatest: () => {
|
||||
if (latest.data) {
|
||||
main.store.add(latest.data);
|
||||
latest.store.clear();
|
||||
main.add(latest.data);
|
||||
latest.clear();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { RequestBuilder, System } from "System";
|
||||
import { FlatNoteStore, NoteStore, StoreSnapshot } from "System/NoteCollection";
|
||||
import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection";
|
||||
import { unwrap } from "Util";
|
||||
|
||||
const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>(
|
||||
@ -28,10 +28,6 @@ const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TSto
|
||||
release();
|
||||
};
|
||||
};
|
||||
const emptyStore = {
|
||||
data: undefined,
|
||||
store: new FlatNoteStore(),
|
||||
} as StoreSnapshot<TSnapshot>;
|
||||
const getState = (): StoreSnapshot<TSnapshot> => {
|
||||
if (rb?.id) {
|
||||
const feed = System.GetFeed(rb.id);
|
||||
@ -39,7 +35,7 @@ const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TSto
|
||||
return unwrap(feed).snapshot as StoreSnapshot<TSnapshot>;
|
||||
}
|
||||
}
|
||||
return emptyStore;
|
||||
return EmptySnapshot as StoreSnapshot<TSnapshot>;
|
||||
};
|
||||
return useSyncExternalStore<StoreSnapshot<TSnapshot>>(
|
||||
v => subscribe(v),
|
||||
|
@ -3,9 +3,22 @@ import { findTag } from "Util";
|
||||
|
||||
export interface StoreSnapshot<TSnapshot> {
|
||||
data: TSnapshot | undefined;
|
||||
store: NoteStore;
|
||||
clear: () => void;
|
||||
loading: () => boolean;
|
||||
add: (ev: Readonly<TaggedRawEvent> | Readonly<Array<TaggedRawEvent>>) => void;
|
||||
}
|
||||
|
||||
export const EmptySnapshot = {
|
||||
data: undefined,
|
||||
clear: () => {
|
||||
// empty
|
||||
},
|
||||
loading: () => true,
|
||||
add: (ev: Readonly<TaggedRawEvent> | Readonly<Array<TaggedRawEvent>>) => {
|
||||
// empty
|
||||
},
|
||||
} as StoreSnapshot<FlatNoteStore>;
|
||||
|
||||
export type NoteStoreSnapshotData = Readonly<Array<TaggedRawEvent>> | Readonly<TaggedRawEvent>;
|
||||
export type NoteStoreHook = () => void;
|
||||
export type NoteStoreHookRelease = () => void;
|
||||
@ -20,7 +33,6 @@ export type OnEoseCallbackRelease = () => void;
|
||||
export abstract class NoteStore {
|
||||
abstract add(ev: Readonly<TaggedRawEvent> | Readonly<Array<TaggedRawEvent>>): void;
|
||||
abstract clear(): void;
|
||||
abstract eose(c: string): void;
|
||||
|
||||
// react hooks
|
||||
abstract hook(cb: NoteStoreHook): NoteStoreHookRelease;
|
||||
@ -28,7 +40,6 @@ export abstract class NoteStore {
|
||||
|
||||
// events
|
||||
abstract onEvent(cb: OnEventCallback): OnEventCallbackRelease;
|
||||
abstract onEose(cb: OnEoseCallback): OnEoseCallback;
|
||||
|
||||
abstract get snapshot(): StoreSnapshot<NoteStoreSnapshotData>;
|
||||
abstract get loading(): boolean;
|
||||
@ -38,10 +49,11 @@ export abstract class NoteStore {
|
||||
export abstract class HookedNoteStore<TSnapshot extends NoteStoreSnapshotData> implements NoteStore {
|
||||
#hooks: Array<NoteStoreHook> = [];
|
||||
#eventHooks: Array<OnEventCallback> = [];
|
||||
#eoseHooks: Array<OnEoseCallback> = [];
|
||||
#loading = true;
|
||||
#storeSnapshot: StoreSnapshot<TSnapshot> = {
|
||||
store: this,
|
||||
clear: () => this.clear(),
|
||||
loading: () => this.loading,
|
||||
add: ev => this.add(ev),
|
||||
data: undefined,
|
||||
};
|
||||
#needsSnapshot = true;
|
||||
@ -60,15 +72,9 @@ export abstract class HookedNoteStore<TSnapshot extends NoteStoreSnapshotData> i
|
||||
this.onChange([]);
|
||||
}
|
||||
|
||||
abstract add(ev: TaggedRawEvent | Array<TaggedRawEvent>): void;
|
||||
abstract add(ev: Readonly<TaggedRawEvent> | Readonly<Array<TaggedRawEvent>>): void;
|
||||
abstract clear(): void;
|
||||
|
||||
eose(c: string): void {
|
||||
for (const hkE of this.#eoseHooks) {
|
||||
hkE(c);
|
||||
}
|
||||
}
|
||||
|
||||
hook(cb: NoteStoreHook): NoteStoreHookRelease {
|
||||
this.#hooks.push(cb);
|
||||
return () => {
|
||||
@ -90,14 +96,6 @@ export abstract class HookedNoteStore<TSnapshot extends NoteStoreSnapshotData> i
|
||||
};
|
||||
}
|
||||
|
||||
onEose(cb: OnEoseCallback): OnEoseCallback {
|
||||
this.#eoseHooks.push(cb);
|
||||
return () => {
|
||||
const idx = this.#eoseHooks.findIndex(a => a === cb);
|
||||
this.#eoseHooks.splice(idx, 1);
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract takeSnapshot(): TSnapshot | undefined;
|
||||
|
||||
protected onChange(changes: Readonly<Array<TaggedRawEvent>>): void {
|
||||
@ -115,8 +113,8 @@ export abstract class HookedNoteStore<TSnapshot extends NoteStoreSnapshotData> i
|
||||
#updateSnapshot() {
|
||||
if (this.#needsSnapshot) {
|
||||
this.#storeSnapshot = {
|
||||
...this.#storeSnapshot,
|
||||
data: this.takeSnapshot(),
|
||||
store: this,
|
||||
};
|
||||
this.#needsSnapshot = false;
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ export class Query {
|
||||
*/
|
||||
#sentToRelays: Array<Readonly<Connection>> = [];
|
||||
|
||||
/**
|
||||
* When each relay returned EOSE
|
||||
*/
|
||||
#eoseRelays: Map<string, number> = new Map();
|
||||
|
||||
/**
|
||||
* Leave the query open until its removed
|
||||
*/
|
||||
@ -88,4 +93,32 @@ export class Query {
|
||||
c.CloseReq(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
eose(sub: string, relay: string) {
|
||||
if (sub === this.id) {
|
||||
console.debug(`[EOSE][${sub}] ${relay}`);
|
||||
this.#eoseRelays.set(relay, unixNowMs());
|
||||
} else {
|
||||
const subQ = this.subQueries.find(a => a.id === sub);
|
||||
if (subQ) {
|
||||
subQ.eose(sub, relay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the progress to EOSE, can be used to determine when we should load more content
|
||||
*/
|
||||
get progress() {
|
||||
const thisProgress = this.#eoseRelays.size / this.#sentToRelays.reduce((acc, v) => (acc += v.Down ? 0 : 1), 0);
|
||||
if (this.subQueries.length === 0) {
|
||||
return thisProgress;
|
||||
}
|
||||
|
||||
let totalProgress = thisProgress;
|
||||
for (const sq of this.subQueries) {
|
||||
totalProgress += sq.progress;
|
||||
}
|
||||
return totalProgress / (this.subQueries.length + 1);
|
||||
}
|
||||
}
|
||||
|
@ -112,11 +112,11 @@ export class NostrSystem {
|
||||
OnEndOfStoredEvents(c: Connection, sub: string) {
|
||||
const q = this.GetQuery(sub);
|
||||
if (q) {
|
||||
q.request.finished = unixNowMs();
|
||||
const f = this.Feeds.get(sub);
|
||||
q.eose(sub, c.Address);
|
||||
const f = this.Feeds.get(q.id);
|
||||
if (f) {
|
||||
f.eose(c.Address);
|
||||
f.loading = false;
|
||||
f.loading = q.progress <= 0.5;
|
||||
console.debug(`${sub} loading=${f.loading}, progress=${q.progress}`);
|
||||
}
|
||||
if (!q.leaveOpen) {
|
||||
c.CloseReq(sub);
|
||||
@ -255,8 +255,6 @@ export class NostrSystem {
|
||||
this.Queries.set(rb.id, q);
|
||||
const store = new type();
|
||||
this.Feeds.set(rb.id, store);
|
||||
store.onEose(c => console.debug(`[EOSE][${rb.id}]: ${c}`));
|
||||
|
||||
this.SendQuery(q);
|
||||
this.#changed();
|
||||
return store;
|
||||
|
@ -60,6 +60,7 @@ export class Connection {
|
||||
Authed: boolean;
|
||||
Ephemeral: boolean;
|
||||
EphemeralTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
Down = true;
|
||||
|
||||
constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) {
|
||||
this.Id = uuid();
|
||||
@ -143,6 +144,7 @@ export class Connection {
|
||||
OnOpen() {
|
||||
this.ConnectTimeout = DefaultConnectTimeout;
|
||||
console.log(`[${this.Address}] Open!`);
|
||||
this.Down = false;
|
||||
this.OnConnected?.();
|
||||
}
|
||||
|
||||
@ -300,6 +302,7 @@ export class Connection {
|
||||
CloseReq(id: string) {
|
||||
if (this.ActiveRequests.delete(id)) {
|
||||
this.#SendJson(["CLOSE", id]);
|
||||
this.OnEose?.(id);
|
||||
this.#SendQueuedRequests();
|
||||
}
|
||||
}
|
||||
@ -319,6 +322,10 @@ export class Connection {
|
||||
}
|
||||
|
||||
#ResetQueues() {
|
||||
//send EOSE on disconnect for active subs
|
||||
this.ActiveRequests.forEach(v => this.OnEose?.(v))
|
||||
this.PendingRequests.forEach(v => this.OnEose?.(v[1]));
|
||||
|
||||
this.ActiveRequests.clear();
|
||||
this.PendingRequests = [];
|
||||
this.PendingRaw = [];
|
||||
|
Loading…
x
Reference in New Issue
Block a user