Ensure relations are loaded throughout the API where needed

This commit is contained in:
Alex Gleason 2023-12-10 16:21:18 -06:00
parent 733b8ba9c5
commit a5369d9826
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
7 changed files with 44 additions and 21 deletions

View file

@ -59,7 +59,7 @@ const createAccountController: AppController = async (c) => {
const verifyCredentialsController: AppController = async (c) => { const verifyCredentialsController: AppController = async (c) => {
const pubkey = c.get('pubkey')!; const pubkey = c.get('pubkey')!;
const event = await getAuthor(pubkey); const event = await getAuthor(pubkey, { relations: ['author_stats'] });
if (event) { if (event) {
return c.json(await renderAccount(event, { withSource: true })); return c.json(await renderAccount(event, { withSource: true }));
} else { } else {
@ -138,7 +138,15 @@ const accountStatusesController: AppController = async (c) => {
return c.json([]); return c.json([]);
} }
const filter: DittoFilter<1> = { authors: [pubkey], kinds: [1], relations: ['author'], since, until, limit }; const filter: DittoFilter<1> = {
authors: [pubkey],
kinds: [1],
relations: ['author', 'event_stats', 'author_stats'],
since,
until,
limit,
};
if (tagged) { if (tagged) {
filter['#t'] = [tagged]; filter['#t'] = [tagged];
} }
@ -257,7 +265,9 @@ const favouritesController: AppController = async (c) => {
.map((event) => event.tags.find((tag) => tag[0] === 'e')?.[1]) .map((event) => event.tags.find((tag) => tag[0] === 'e')?.[1])
.filter((id): id is string => !!id); .filter((id): id is string => !!id);
const events1 = await mixer.getFilters([{ kinds: [1], ids, relations: ['author'] }], { timeout: Time.seconds(1) }); const events1 = await mixer.getFilters([{ kinds: [1], ids, relations: ['author', 'event_stats', 'author_stats'] }], {
timeout: Time.seconds(1),
});
const statuses = await Promise.all(events1.map((event) => renderStatus(event, c.get('pubkey')))); const statuses = await Promise.all(events1.map((event) => renderStatus(event, c.get('pubkey'))));
return paginated(c, events1, statuses); return paginated(c, events1, statuses);

View file

@ -69,7 +69,7 @@ function searchEvents({ q, type, limit, account_id }: SearchQuery): Promise<Even
const filter: DittoFilter = { const filter: DittoFilter = {
kinds: typeToKinds(type), kinds: typeToKinds(type),
search: q, search: q,
relations: ['author'], relations: ['author', 'event_stats', 'author_stats'],
limit, limit,
}; };
@ -115,16 +115,20 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery): Promise<Ditt
const result = nip19.decode(q); const result = nip19.decode(q);
switch (result.type) { switch (result.type) {
case 'npub': case 'npub':
if (accounts) filters.push({ kinds: [0], authors: [result.data] }); if (accounts) filters.push({ kinds: [0], authors: [result.data], relations: ['author_stats'] });
break; break;
case 'nprofile': case 'nprofile':
if (accounts) filters.push({ kinds: [0], authors: [result.data.pubkey] }); if (accounts) filters.push({ kinds: [0], authors: [result.data.pubkey], relations: ['author_stats'] });
break; break;
case 'note': case 'note':
if (statuses) filters.push({ kinds: [1], ids: [result.data] }); if (statuses) {
filters.push({ kinds: [1], ids: [result.data], relations: ['author', 'event_stats', 'author_stats'] });
}
break; break;
case 'nevent': case 'nevent':
if (statuses) filters.push({ kinds: [1], ids: [result.data.id] }); if (statuses) {
filters.push({ kinds: [1], ids: [result.data.id], relations: ['author', 'event_stats', 'author_stats'] });
}
break; break;
} }
} catch (_e) { } catch (_e) {
@ -136,11 +140,11 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery): Promise<Ditt
} else if (accounts && ACCT_REGEX.test(q)) { } else if (accounts && ACCT_REGEX.test(q)) {
const pubkey = await lookupNip05Cached(q); const pubkey = await lookupNip05Cached(q);
if (pubkey) { if (pubkey) {
filters.push({ kinds: [0], authors: [pubkey] }); filters.push({ kinds: [0], authors: [pubkey], relations: ['author_stats'] });
} }
} }
return filters.map((filter) => ({ ...filter, relations: ['author'] })); return filters;
} }
export { searchController }; export { searchController };

View file

@ -29,7 +29,7 @@ const createStatusSchema = z.object({
const statusController: AppController = async (c) => { const statusController: AppController = async (c) => {
const id = c.req.param('id'); const id = c.req.param('id');
const event = await getEvent(id, { kind: 1, relations: ['author'] }); const event = await getEvent(id, { kind: 1, relations: ['author', 'event_stats', 'author_stats'] });
if (event) { if (event) {
return c.json(await renderStatus(event, c.get('pubkey'))); return c.json(await renderStatus(event, c.get('pubkey')));
} }
@ -89,7 +89,7 @@ const createStatusController: AppController = async (c) => {
const contextController: AppController = async (c) => { const contextController: AppController = async (c) => {
const id = c.req.param('id'); const id = c.req.param('id');
const event = await getEvent(id, { kind: 1, relations: ['author'] }); const event = await getEvent(id, { kind: 1, relations: ['author', 'event_stats', 'author_stats'] });
async function renderStatuses(events: Event<1>[]) { async function renderStatuses(events: Event<1>[]) {
const statuses = await Promise.all(events.map((event) => renderStatus(event, c.get('pubkey')))); const statuses = await Promise.all(events.map((event) => renderStatus(event, c.get('pubkey'))));
@ -110,7 +110,7 @@ const contextController: AppController = async (c) => {
const favouriteController: AppController = async (c) => { const favouriteController: AppController = async (c) => {
const id = c.req.param('id'); const id = c.req.param('id');
const target = await getEvent(id, { kind: 1, relations: ['author'] }); const target = await getEvent(id, { kind: 1, relations: ['author', 'event_stats', 'author_stats'] });
if (target) { if (target) {
await createEvent({ await createEvent({

View file

@ -35,7 +35,7 @@ const hashtagTimelineController: AppController = (c) => {
/** Render statuses for timelines. */ /** Render statuses for timelines. */
async function renderStatuses(c: AppContext, filters: DittoFilter<1>[]) { async function renderStatuses(c: AppContext, filters: DittoFilter<1>[]) {
const events = await mixer.getFilters( const events = await mixer.getFilters(
filters.map((filter) => ({ ...filter, relations: ['author'] })), filters.map((filter) => ({ ...filter, relations: ['author', 'event_stats', 'author_stats'] })),
{ timeout: Time.seconds(1) }, { timeout: Time.seconds(1) },
); );

View file

@ -27,8 +27,14 @@ const getEvent = async <K extends number = number>(
}; };
/** Get a Nostr `set_medatadata` event for a user's pubkey. */ /** Get a Nostr `set_medatadata` event for a user's pubkey. */
const getAuthor = async (pubkey: string, timeout = 1000): Promise<Event<0> | undefined> => { const getAuthor = async (pubkey: string, opts: GetEventOpts<0> = {}): Promise<Event<0> | undefined> => {
const [event] = await mixer.getFilters([{ authors: [pubkey], kinds: [0], limit: 1 }], { limit: 1, timeout }); const { relations, timeout = 1000 } = opts;
const [event] = await mixer.getFilters(
[{ authors: [pubkey], relations, kinds: [0], limit: 1 }],
{ limit: 1, timeout },
);
return event; return event;
}; };
@ -60,7 +66,7 @@ async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise
const inReplyTo = replyTag ? replyTag[1] : undefined; const inReplyTo = replyTag ? replyTag[1] : undefined;
if (inReplyTo) { if (inReplyTo) {
const parentEvent = await getEvent(inReplyTo, { kind: 1, relations: ['author'] }); const parentEvent = await getEvent(inReplyTo, { kind: 1, relations: ['author', 'event_stats', 'author_stats'] });
if (parentEvent) { if (parentEvent) {
result.push(parentEvent); result.push(parentEvent);
@ -73,7 +79,10 @@ async function getAncestors(event: Event<1>, result = [] as Event<1>[]): Promise
} }
function getDescendants(eventId: string): Promise<Event<1>[]> { function getDescendants(eventId: string): Promise<Event<1>[]> {
return mixer.getFilters([{ kinds: [1], '#e': [eventId], relations: ['author'] }], { limit: 200, timeout: 2000 }); return mixer.getFilters(
[{ kinds: [1], '#e': [eventId], relations: ['author', 'event_stats', 'author_stats'] }],
{ limit: 200, timeout: 2000 },
);
} }
/** Returns whether the pubkey is followed by a local user. */ /** Returns whether the pubkey is followed by a local user. */

View file

@ -15,7 +15,7 @@ async function renderEventAccounts(c: AppContext, filters: Filter[]) {
} }
const accounts = await Promise.all([...pubkeys].map(async (pubkey) => { const accounts = await Promise.all([...pubkeys].map(async (pubkey) => {
const author = await getAuthor(pubkey); const author = await getAuthor(pubkey, { relations: ['author_stats'] });
if (author) { if (author) {
return renderAccount(author); return renderAccount(author);
} }

View file

@ -86,8 +86,8 @@ async function renderStatus(event: eventsDB.DittoEvent<1>, viewerPubkey?: string
} }
async function toMention(pubkey: string) { async function toMention(pubkey: string) {
const profile = await getAuthor(pubkey); const author = await getAuthor(pubkey);
const account = profile ? await renderAccount(profile) : undefined; const account = author ? await renderAccount(author) : undefined;
if (account) { if (account) {
return { return {