move for you feed creation to worker
This commit is contained in:
parent
351a249a32
commit
5bc3c10d36
@ -1,16 +1,15 @@
|
|||||||
import { EventKind, TaggedNostrEvent } from "@snort/system";
|
import {EventKind, NostrEvent} from "@snort/system";
|
||||||
import { memo, useEffect, useMemo, useState } from "react";
|
import { memo, useEffect, useMemo, useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
import {Relay} from "@/Cache";
|
||||||
import { DisplayAs, DisplayAsSelector } from "@/Components/Feed/DisplayAsSelector";
|
import { DisplayAs, DisplayAsSelector } from "@/Components/Feed/DisplayAsSelector";
|
||||||
import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
|
import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
|
||||||
import { TaskList } from "@/Components/Tasks/TaskList";
|
import { TaskList } from "@/Components/Tasks/TaskList";
|
||||||
import { getForYouFeed } from "@/Db/getForYouFeed";
|
|
||||||
import useTimelineFeed, { TimelineFeedOptions, TimelineSubject } from "@/Feed/TimelineFeed";
|
import useTimelineFeed, { TimelineFeedOptions, TimelineSubject } from "@/Feed/TimelineFeed";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import messages from "@/Pages/messages";
|
import messages from "@/Pages/messages";
|
||||||
import { System } from "@/system";
|
|
||||||
|
|
||||||
const FollowsHint = () => {
|
const FollowsHint = () => {
|
||||||
const { publicKey: pubKey, follows } = useLogin();
|
const { publicKey: pubKey, follows } = useLogin();
|
||||||
@ -32,14 +31,14 @@ const FollowsHint = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let forYouFeed = {
|
let forYouFeed = {
|
||||||
events: [] as TaggedNostrEvent[],
|
events: [] as NostrEvent[],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let getForYouFeedPromise: Promise<TaggedNostrEvent[]> | null = null;
|
let getForYouFeedPromise: Promise<NostrEvent[]> | null = null;
|
||||||
|
|
||||||
export const ForYouTab = memo(function ForYouTab() {
|
export const ForYouTab = memo(function ForYouTab() {
|
||||||
const [notes, setNotes] = useState<TaggedNostrEvent[]>(forYouFeed.events);
|
const [notes, setNotes] = useState<NostrEvent[]>(forYouFeed.events);
|
||||||
const { feedDisplayAs } = useLogin();
|
const { feedDisplayAs } = useLogin();
|
||||||
const displayAsInitial = feedDisplayAs ?? "list";
|
const displayAsInitial = feedDisplayAs ?? "list";
|
||||||
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
const [displayAs, setDisplayAs] = useState<DisplayAs>(displayAsInitial);
|
||||||
@ -64,7 +63,7 @@ export const ForYouTab = memo(function ForYouTab() {
|
|||||||
const latestFeed = useTimelineFeed(subject, { method: "TIME_RANGE" } as TimelineFeedOptions);
|
const latestFeed = useTimelineFeed(subject, { method: "TIME_RANGE" } as TimelineFeedOptions);
|
||||||
const filteredLatestFeed = useMemo(() => {
|
const filteredLatestFeed = useMemo(() => {
|
||||||
// no replies
|
// no replies
|
||||||
return latestFeed.main?.filter((ev: TaggedNostrEvent) => !ev.tags.some((tag: string[]) => tag[0] === "e")) ?? [];
|
return latestFeed.main?.filter((ev: NostrEvent) => !ev.tags.some((tag: string[]) => tag[0] === "e")) ?? [];
|
||||||
}, [latestFeed.main]);
|
}, [latestFeed.main]);
|
||||||
|
|
||||||
const getFeed = () => {
|
const getFeed = () => {
|
||||||
@ -72,13 +71,13 @@ export const ForYouTab = memo(function ForYouTab() {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (!getForYouFeedPromise) {
|
if (!getForYouFeedPromise) {
|
||||||
getForYouFeedPromise = getForYouFeed(publicKey);
|
getForYouFeedPromise = Relay.forYouFeed(publicKey);
|
||||||
}
|
}
|
||||||
getForYouFeedPromise!.then(notes => {
|
getForYouFeedPromise!.then(notes => {
|
||||||
getForYouFeedPromise = null;
|
getForYouFeedPromise = null;
|
||||||
if (notes.length < 10) {
|
if (notes.length < 10) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getForYouFeed(publicKey);
|
getForYouFeedPromise = Relay.forYouFeed(publicKey);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
forYouFeed = {
|
forYouFeed = {
|
||||||
@ -86,11 +85,6 @@ export const ForYouTab = memo(function ForYouTab() {
|
|||||||
created_at: Date.now(),
|
created_at: Date.now(),
|
||||||
};
|
};
|
||||||
setNotes(notes);
|
setNotes(notes);
|
||||||
notes.forEach(note => {
|
|
||||||
queueMicrotask(() => {
|
|
||||||
System.HandleEvent(note);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,49 +1,48 @@
|
|||||||
import { NostrEvent, parseZap } from "@snort/system";
|
import { NostrEvent, parseZap } from "@snort/system";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { Relay } from "@/Cache";
|
import {RelayHandler} from "./types";
|
||||||
|
|
||||||
const log = debug("getForYouFeed");
|
const log = debug("getForYouFeed");
|
||||||
|
|
||||||
export async function getForYouFeed(pubkey: string): Promise<NostrEvent[]> {
|
export async function getForYouFeed(relay: RelayHandler, pubkey: string): Promise<NostrEvent[]> {
|
||||||
console.time("For You feed generation time");
|
console.time("For You feed generation time");
|
||||||
|
|
||||||
log("pubkey", pubkey);
|
log("pubkey", pubkey);
|
||||||
|
|
||||||
// Get events reacted to by me
|
// Get events reacted to by me
|
||||||
const myReactedEventIds = await getMyReactedEvents(pubkey);
|
const myReactedEventIds = await getMyReactedEvents(relay, pubkey);
|
||||||
log("my reacted events", myReactedEventIds);
|
log("my reacted events", myReactedEventIds);
|
||||||
|
|
||||||
const myReactedAuthors = await getMyReactedAuthors(myReactedEventIds, pubkey);
|
const myReactedAuthors = await getMyReactedAuthors(relay, myReactedEventIds, pubkey);
|
||||||
log("my reacted authors", myReactedAuthors);
|
log("my reacted authors", myReactedAuthors);
|
||||||
|
|
||||||
// Get others who reacted to the same events as me
|
// Get others who reacted to the same events as me
|
||||||
const othersWhoReacted = await getOthersWhoReacted(myReactedEventIds, pubkey);
|
const othersWhoReacted = await getOthersWhoReacted(relay, myReactedEventIds, pubkey);
|
||||||
// this tends to be small when the user has just logged in, we should maybe subscribe for more from relays
|
// this tends to be small when the user has just logged in, we should maybe subscribe for more from relays
|
||||||
log("others who reacted", othersWhoReacted);
|
log("others who reacted", othersWhoReacted);
|
||||||
|
|
||||||
// Get event ids reacted to by those others
|
// Get event ids reacted to by those others
|
||||||
const reactedByOthers = await getEventIdsReactedByOthers(othersWhoReacted, myReactedEventIds, pubkey);
|
const reactedByOthers = await getEventIdsReactedByOthers(relay, othersWhoReacted, myReactedEventIds, pubkey);
|
||||||
log("reacted by others", reactedByOthers);
|
log("reacted by others", reactedByOthers);
|
||||||
|
|
||||||
// Get full events in sorted order
|
// Get full events in sorted order
|
||||||
const feed = await getFeedEvents(reactedByOthers, myReactedAuthors);
|
const feed = await getFeedEvents(relay, reactedByOthers, myReactedAuthors);
|
||||||
log("feed.length", feed.length);
|
log("feed.length", feed.length);
|
||||||
|
|
||||||
console.timeEnd("For You feed generation time");
|
console.timeEnd("For You feed generation time");
|
||||||
return feed;
|
return feed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMyReactedAuthors(myReactedEventIds: Set<string>, myPubkey: string) {
|
async function getMyReactedAuthors(relay: RelayHandler, myReactedEventIds: Set<string>, myPubkey: string) {
|
||||||
const myReactedAuthors = new Map<string, number>();
|
const myReactedAuthors = new Map<string, number>();
|
||||||
|
|
||||||
const myReactions = await Relay.query([
|
const myReactions = relay.req(
|
||||||
"REQ",
|
|
||||||
"getMyReactedAuthors",
|
"getMyReactedAuthors",
|
||||||
{
|
{
|
||||||
"#e": Array.from(myReactedEventIds),
|
"#e": Array.from(myReactedEventIds),
|
||||||
},
|
},
|
||||||
]);
|
) as NostrEvent[];
|
||||||
|
|
||||||
myReactions.forEach(reaction => {
|
myReactions.forEach(reaction => {
|
||||||
if (reaction.pubkey !== myPubkey) {
|
if (reaction.pubkey !== myPubkey) {
|
||||||
@ -54,17 +53,16 @@ async function getMyReactedAuthors(myReactedEventIds: Set<string>, myPubkey: str
|
|||||||
return myReactedAuthors;
|
return myReactedAuthors;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMyReactedEvents(pubkey: string) {
|
async function getMyReactedEvents(relay: RelayHandler, pubkey: string) {
|
||||||
const myReactedEventIds = new Set<string>();
|
const myReactedEventIds = new Set<string>();
|
||||||
|
|
||||||
const myEvents = await Relay.query([
|
const myEvents = relay.req(
|
||||||
"REQ",
|
|
||||||
"getMyReactedEventIds",
|
"getMyReactedEventIds",
|
||||||
{
|
{
|
||||||
authors: [pubkey],
|
authors: [pubkey],
|
||||||
kinds: [1, 6, 7, 9735],
|
kinds: [1, 6, 7, 9735],
|
||||||
},
|
},
|
||||||
]);
|
) as NostrEvent[];
|
||||||
myEvents.forEach(ev => {
|
myEvents.forEach(ev => {
|
||||||
const targetEventId = ev.kind === 9735 ? parseZap(ev).event?.id : ev.tags.find(tag => tag[0] === "e")?.[1];
|
const targetEventId = ev.kind === 9735 ? parseZap(ev).event?.id : ev.tags.find(tag => tag[0] === "e")?.[1];
|
||||||
if (targetEventId) {
|
if (targetEventId) {
|
||||||
@ -75,16 +73,15 @@ async function getMyReactedEvents(pubkey: string) {
|
|||||||
return myReactedEventIds;
|
return myReactedEventIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getOthersWhoReacted(myReactedEventIds: Set<string>, myPubkey: string) {
|
async function getOthersWhoReacted(relay: RelayHandler, myReactedEventIds: Set<string>, myPubkey: string) {
|
||||||
const othersWhoReacted = new Map<string, number>();
|
const othersWhoReacted = new Map<string, number>();
|
||||||
|
|
||||||
const otherReactions = await Relay.query([
|
const otherReactions = relay.req(
|
||||||
"REQ",
|
|
||||||
"getOthersWhoReacted",
|
"getOthersWhoReacted",
|
||||||
{
|
{
|
||||||
"#e": Array.from(myReactedEventIds),
|
"#e": Array.from(myReactedEventIds),
|
||||||
},
|
},
|
||||||
]);
|
) as NostrEvent[];
|
||||||
|
|
||||||
otherReactions.forEach(reaction => {
|
otherReactions.forEach(reaction => {
|
||||||
if (reaction.pubkey !== myPubkey) {
|
if (reaction.pubkey !== myPubkey) {
|
||||||
@ -96,20 +93,20 @@ async function getOthersWhoReacted(myReactedEventIds: Set<string>, myPubkey: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getEventIdsReactedByOthers(
|
async function getEventIdsReactedByOthers(
|
||||||
|
relay: RelayHandler,
|
||||||
othersWhoReacted: Map<string, number>,
|
othersWhoReacted: Map<string, number>,
|
||||||
myReactedEvents: Set<string>,
|
myReactedEvents: Set<string>,
|
||||||
myPub: string,
|
myPub: string,
|
||||||
) {
|
) {
|
||||||
const eventIdsReactedByOthers = new Map<string, number>();
|
const eventIdsReactedByOthers = new Map<string, number>();
|
||||||
|
|
||||||
const events = await Relay.query([
|
const events = relay.req(
|
||||||
"REQ",
|
|
||||||
"getEventIdsReactedByOthers",
|
"getEventIdsReactedByOthers",
|
||||||
{
|
{
|
||||||
authors: [...othersWhoReacted.keys()],
|
authors: [...othersWhoReacted.keys()],
|
||||||
kinds: [1, 6, 7, 9735],
|
kinds: [1, 6, 7, 9735],
|
||||||
},
|
},
|
||||||
]);
|
) as NostrEvent[];
|
||||||
|
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (event.pubkey === myPub || myReactedEvents.has(event.id)) {
|
if (event.pubkey === myPub || myReactedEvents.has(event.id)) {
|
||||||
@ -127,16 +124,12 @@ async function getEventIdsReactedByOthers(
|
|||||||
return eventIdsReactedByOthers;
|
return eventIdsReactedByOthers;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFeedEvents(reactedToIds: Map<string, number>, reactedToAuthors: Map<string, number>) {
|
async function getFeedEvents(relay: RelayHandler, reactedToIds: Map<string, number>, reactedToAuthors: Map<string, number>) {
|
||||||
const events = await Relay.query([
|
const events = relay.sql(
|
||||||
"REQ",
|
`select json from events where id in (${Array.from(reactedToIds.keys()).map(() => "?").join(", ")}) and kind = 1 order by seen_at ASC, created DESC limit 1000`,
|
||||||
"getFeedEvents",
|
Array.from(reactedToIds.keys()),
|
||||||
{
|
).map(row => JSON.parse(row[0] as string) as NostrEvent);
|
||||||
ids: Array.from(reactedToIds.keys()),
|
|
||||||
kinds: [1],
|
|
||||||
since: Math.floor(Date.now() / 1000) - 60 * 60 * 24 * 7,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const seen = new Set<string>(events.map(ev => ev.id));
|
const seen = new Set<string>(events.map(ev => ev.id));
|
||||||
|
|
||||||
log("reactedToAuthors", reactedToAuthors);
|
log("reactedToAuthors", reactedToAuthors);
|
||||||
@ -145,16 +138,11 @@ async function getFeedEvents(reactedToIds: Map<string, number>, reactedToAuthors
|
|||||||
.sort((a, b) => reactedToAuthors.get(b)! - reactedToAuthors.get(a)!)
|
.sort((a, b) => reactedToAuthors.get(b)! - reactedToAuthors.get(a)!)
|
||||||
.slice(20);
|
.slice(20);
|
||||||
|
|
||||||
const eventsByFavoriteAuthors = await Relay.query([
|
const eventsByFavoriteAuthors = relay.sql(
|
||||||
"REQ",
|
`select json from events where pubkey in (${favoriteAuthors.map(() => "?").join(", ")}) and kind = 1 order by seen_at ASC, created DESC limit 100`,
|
||||||
"getFeedEvents",
|
favoriteAuthors,
|
||||||
{
|
).map(row => JSON.parse(row[0] as string) as NostrEvent);
|
||||||
authors: favoriteAuthors,
|
|
||||||
kinds: [1],
|
|
||||||
since: Math.floor(Date.now() / 1000) - 60 * 60 * 24,
|
|
||||||
limit: 100,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
eventsByFavoriteAuthors.forEach(ev => {
|
eventsByFavoriteAuthors.forEach(ev => {
|
||||||
if (!seen.has(ev.id)) {
|
if (!seen.has(ev.id)) {
|
@ -45,6 +45,10 @@ export class WorkerRelayInterface {
|
|||||||
return await this.#workerRpc<void, Uint8Array>("dumpDb");
|
return await this.#workerRpc<void, Uint8Array>("dumpDb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async forYouFeed(pubkey: string) {
|
||||||
|
return await this.#workerRpc<string, Array<NostrEvent>>("forYouFeed", pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
#workerRpc<T, R>(cmd: WorkerMessageCommand, args?: T) {
|
#workerRpc<T, R>(cmd: WorkerMessageCommand, args?: T) {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const msg = {
|
const msg = {
|
||||||
|
@ -9,7 +9,8 @@ export type WorkerMessageCommand =
|
|||||||
| "summary"
|
| "summary"
|
||||||
| "close"
|
| "close"
|
||||||
| "dumpDb"
|
| "dumpDb"
|
||||||
| "emit-event";
|
| "emit-event"
|
||||||
|
| "forYouFeed";
|
||||||
|
|
||||||
export interface WorkerMessage<T> {
|
export interface WorkerMessage<T> {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -4,6 +4,7 @@ import { InMemoryRelay } from "./memory-relay";
|
|||||||
import { WorkQueueItem, barrierQueue, processWorkQueue } from "./queue";
|
import { WorkQueueItem, barrierQueue, processWorkQueue } from "./queue";
|
||||||
import { SqliteRelay } from "./sqlite-relay";
|
import { SqliteRelay } from "./sqlite-relay";
|
||||||
import { NostrEvent, RelayHandler, ReqCommand, ReqFilter, WorkerMessage, unixNowMs } from "./types";
|
import { NostrEvent, RelayHandler, ReqCommand, ReqFilter, WorkerMessage, unixNowMs } from "./types";
|
||||||
|
import {getForYouFeed} from "./forYouFeed";
|
||||||
|
|
||||||
let relay: RelayHandler | undefined;
|
let relay: RelayHandler | undefined;
|
||||||
|
|
||||||
@ -130,6 +131,13 @@ globalThis.onmessage = async ev => {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "forYouFeed": {
|
||||||
|
await barrierQueue(cmdQueue, async () => {
|
||||||
|
const res = await getForYouFeed(relay!, msg.args as string);
|
||||||
|
reply(msg.id, res);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
reply(msg.id, { error: "Unknown command" });
|
reply(msg.id, { error: "Unknown command" });
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user