slight changes to timeline loader

This commit is contained in:
hzrd149
2023-03-11 08:52:12 -06:00
parent 9cb2e8a5d8
commit a4194c1a73
7 changed files with 28 additions and 31 deletions

View File

@@ -72,6 +72,7 @@ I would recomend you use a browser extension like [Alby](https://getalby.com/) o
## TODO ## TODO
- Update TimelineLoader to connect to each relay individually so it better track the latest events
- Create notifications service that keeps track of read notifications. (show unread count in sidenav) - Create notifications service that keeps track of read notifications. (show unread count in sidenav)
- Rebuild relays view to show relay info and settings NIP-11 - Rebuild relays view to show relay info and settings NIP-11
- filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers) - filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers)
@@ -81,8 +82,6 @@ I would recomend you use a browser extension like [Alby](https://getalby.com/) o
- make app a valid web share target https://developer.chrome.com/articles/web-share-target/ - make app a valid web share target https://developer.chrome.com/articles/web-share-target/
- handle image share - handle image share
- implement NIP-56 and blocking - implement NIP-56 and blocking
- allow user to select relay or following list when fetching replies (default to my relays + following?)
- massive thread note1dapvuu8fl09yjtg2gyr2h6nypaffl2sq0aj5raz86463qk5kpyzqlxvtc3
- Improve link previews https://github.com/pengx17/logseq-plugin-link-preview/blob/master/src/use-link-preview-metadata.tsx - Improve link previews https://github.com/pengx17/logseq-plugin-link-preview/blob/master/src/use-link-preview-metadata.tsx
- Support `magnet:` links - Support `magnet:` links
- in-browser video player? https://webtorrent.io/ - in-browser video player? https://webtorrent.io/

View File

@@ -3,44 +3,40 @@ import { NostrEvent } from "../types/nostr-event";
import { NostrQuery } from "../types/nostr-query"; import { NostrQuery } from "../types/nostr-query";
import { NostrRequest } from "./nostr-request"; import { NostrRequest } from "./nostr-request";
import { NostrMultiSubscription } from "./nostr-multi-subscription"; import { NostrMultiSubscription } from "./nostr-multi-subscription";
import Subject, { PersistentSubject } from "./subject"; import { PersistentSubject } from "./subject";
import { utils } from "nostr-tools";
export type NostrQueryWithStart = NostrQuery & { since: number };
type Options = { type Options = {
name?: string; name?: string;
pageSize: number; pageSize: number;
startLimit: number;
}; };
export type TimelineLoaderOptions = Partial<Options>; export type TimelineLoaderOptions = Partial<Options>;
export class TimelineLoader { export class TimelineLoader {
relays: string[]; relays: string[];
query: NostrQueryWithStart; query: NostrQuery;
events = new PersistentSubject<NostrEvent[]>([]); events = new PersistentSubject<NostrEvent[]>([]);
loading = new PersistentSubject(false); loading = new PersistentSubject(false);
page = new PersistentSubject(0); page = new PersistentSubject(0);
private eventDir = new Map<string, NostrEvent>(); private seenEvents = new Set<string>();
private subscription: NostrMultiSubscription; private subscription: NostrMultiSubscription;
private opts: Options = { pageSize: moment.duration(1, "hour").asSeconds() }; private opts: Options = { pageSize: moment.duration(1, "hour").asSeconds(), startLimit: 10 };
constructor(relays: string[], query: NostrQueryWithStart, opts?: TimelineLoaderOptions) {
if (!query.since) throw new Error('Timeline requires "since" to be set in query');
constructor(relays: string[], query: NostrQuery, opts?: TimelineLoaderOptions) {
this.relays = relays; this.relays = relays;
this.query = query;
Object.assign(this.opts, opts); Object.assign(this.opts, opts);
this.query = { ...query, limit: this.opts.startLimit };
this.subscription = new NostrMultiSubscription(relays, query, opts?.name); this.subscription = new NostrMultiSubscription(relays, query, opts?.name);
this.subscription.onEvent.subscribe(this.handleEvent.bind(this)); this.subscription.onEvent.subscribe(this.handleEvent, this);
} }
setQuery(query: NostrQueryWithStart) { setQuery(query: NostrQuery) {
if (!query.since) throw new Error('Timeline requires "since" to be set in query'); this.query = { ...query, limit: this.opts.startLimit };
this.subscription.setQuery(this.query);
this.query = query;
this.subscription.setQuery(query);
} }
setRelays(relays: string[]) { setRelays(relays: string[]) {
@@ -49,15 +45,15 @@ export class TimelineLoader {
} }
private handleEvent(event: NostrEvent) { private handleEvent(event: NostrEvent) {
if (!this.eventDir.has(event.id)) { if (!this.seenEvents.has(event.id)) {
this.eventDir.set(event.id, event); this.seenEvents.add(event.id);
this.events.next(Array.from(this.eventDir.values()).sort((a, b) => b.created_at - a.created_at)); this.events.next(utils.insertEventIntoDescendingList(Array.from(this.events.value), event));
if (this.loading.value) this.loading.next(false); if (this.loading.value) this.loading.next(false);
} }
} }
private getPageDates(page: number) { private getPageDates(page: number) {
const start = this.query.since; const start = this.events.value[0]?.created_at ?? moment().unix();
const until = start - page * this.opts.pageSize; const until = start - page * this.opts.pageSize;
const since = until - this.opts.pageSize; const since = until - this.opts.pageSize;
@@ -84,7 +80,7 @@ export class TimelineLoader {
forgetEvents() { forgetEvents() {
this.events.next([]); this.events.next([]);
this.eventDir.clear(); this.seenEvents.clear();
this.subscription.forgetEvents(); this.subscription.forgetEvents();
} }
open() { open() {

View File

@@ -2,9 +2,11 @@ import { RelayConfig } from "../classes/relay";
import { safeRelayUrl } from "./url"; import { safeRelayUrl } from "./url";
export function normalizeRelayConfigs(relays: RelayConfig[]) { export function normalizeRelayConfigs(relays: RelayConfig[]) {
const seen: string[] = [];
return relays.reduce((newArr, r) => { return relays.reduce((newArr, r) => {
const safeUrl = safeRelayUrl(r.url); const safeUrl = safeRelayUrl(r.url);
if (safeUrl) { if (safeUrl && !seen.includes(safeUrl)) {
seen.push(safeUrl);
newArr.push({ ...r, url: safeUrl }); newArr.push({ ...r, url: safeUrl });
} }
return newArr; return newArr;

View File

@@ -1,13 +1,14 @@
import { useCallback, useEffect, useRef } from "react"; import { useCallback, useEffect, useRef } from "react";
import { useUnmount } from "react-use"; import { useUnmount } from "react-use";
import { NostrQueryWithStart, TimelineLoader, TimelineLoaderOptions } from "../classes/timeline-loader"; import { TimelineLoader, TimelineLoaderOptions } from "../classes/timeline-loader";
import { NostrQuery } from "../types/nostr-query";
import useSubject from "./use-subject"; import useSubject from "./use-subject";
type Options = TimelineLoaderOptions & { type Options = TimelineLoaderOptions & {
enabled?: boolean; enabled?: boolean;
}; };
export function useTimelineLoader(key: string, relays: string[], query: NostrQueryWithStart, opts?: Options) { export function useTimelineLoader(key: string, relays: string[], query: NostrQuery, opts?: Options) {
if (opts && !opts.name) opts.name = key; if (opts && !opts.name) opts.name = key;
const ref = useRef<TimelineLoader | null>(null); const ref = useRef<TimelineLoader | null>(null);

View File

@@ -25,7 +25,7 @@ export default function GlobalTab() {
const { events, loading, loadMore, loader } = useTimelineLoader( const { events, loading, loadMore, loader } = useTimelineLoader(
`global`, `global`,
selectedRelay ? [selectedRelay] : availableRelays, selectedRelay ? [selectedRelay] : availableRelays,
{ kinds: [1], since: moment().unix() }, { kinds: [1] },
{ pageSize: moment.duration(5, "minutes").asSeconds() } { pageSize: moment.duration(5, "minutes").asSeconds() }
); );

View File

@@ -45,7 +45,6 @@ const NotificationsView = () => {
{ {
"#p": [account.pubkey], "#p": [account.pubkey],
kinds: [1], kinds: [1],
since: moment().subtract(1, "day").unix(),
}, },
{ pageSize: moment.duration(1, "day").asSeconds() } { pageSize: moment.duration(1, "day").asSeconds() }
); );

View File

@@ -34,16 +34,16 @@ const UserNotesTab = () => {
.filter((r) => r.mode & RelayMode.WRITE) .filter((r) => r.mode & RelayMode.WRITE)
.map((r) => r.url); .map((r) => r.url);
// merge the users relays with client relays // merge the users relays with client relays
const mergedRelays = useReadRelayUrls(userRelays); const readRelays = useReadRelayUrls();
// find the top 4 // find the top 4
const relays = relayScoreboardService.getRankedRelays(mergedRelays).slice(0, 4); const relays = relayScoreboardService.getRankedRelays(userRelays.length === 0 ? readRelays : userRelays).slice(0, 4);
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure(); const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
const { events, loading, loadMore } = useTimelineLoader( const { events, loading, loadMore } = useTimelineLoader(
`${pubkey}-notes`, `${pubkey}-notes`,
relays, relays,
{ authors: [pubkey], kinds: [1], since: moment().subtract(1, "day").unix() }, { authors: [pubkey], kinds: [1] },
{ pageSize: moment.duration(1, "day").asSeconds() } { pageSize: moment.duration(1, "day").asSeconds() }
); );
const timeline = showReplies ? events : events.filter(isNote); const timeline = showReplies ? events : events.filter(isNote);