diff --git a/src/db/events.ts b/src/db/events.ts index b8db69a..54ed10b 100644 --- a/src/db/events.ts +++ b/src/db/events.ts @@ -80,6 +80,9 @@ type EventQuery = SelectQueryBuilder; /** Build the query for a filter. */ @@ -184,8 +190,12 @@ function getFiltersQuery(filters: DittoFilter[]) { .reduce((result, query) => result.unionAll(query)); } +type AuthorStats = Omit; +type EventStats = Omit; + interface DittoEvent extends Event { - author?: Event<0>; + author?: Event<0> & { stats?: AuthorStats }; + stats?: EventStats; } /** Get events for filters from the database. */ @@ -221,6 +231,22 @@ async function getFilters( tags: JSON.parse(row.author_tags!), sig: row.author_sig!, }; + + if (typeof row.author_stats_followers_count === 'number') { + event.author.stats = { + followers_count: row.author_stats_followers_count, + following_count: row.author_stats_following_count!, + notes_count: row.author_stats_notes_count!, + }; + } + } + + if (typeof row.stats_replies_count === 'number') { + event.stats = { + replies_count: row.stats_replies_count, + reposts_count: row.stats_reposts_count!, + reactions_count: row.stats_reactions_count!, + }; } return event; diff --git a/src/views/mastodon/accounts.ts b/src/views/mastodon/accounts.ts index 3a55fb7..f2d2f58 100644 --- a/src/views/mastodon/accounts.ts +++ b/src/views/mastodon/accounts.ts @@ -1,8 +1,7 @@ import { Conf } from '@/config.ts'; -import * as eventsDB from '@/db/events.ts'; +import { type DittoEvent } from '@/db/events.ts'; import { findUser } from '@/db/users.ts'; import { lodash, nip19, type UnsignedEvent } from '@/deps.ts'; -import { getFollowedPubkeys } from '@/queries.ts'; import { jsonMetaContentSchema } from '@/schemas/nostr.ts'; import { verifyNip05Cached } from '@/utils/nip05.ts'; import { Nip05, nostrDate, nostrNow, parseNip05 } from '@/utils.ts'; @@ -12,7 +11,10 @@ interface ToAccountOpts { withSource?: boolean; } -async function renderAccount(event: UnsignedEvent<0>, opts: ToAccountOpts = {}) { +async function renderAccount( + event: Omit, 'id' | 'sig'>, + opts: ToAccountOpts = {}, +) { const { withSource = false } = opts; const { pubkey } = event; @@ -26,12 +28,9 @@ async function renderAccount(event: UnsignedEvent<0>, opts: ToAccountOpts = {}) const npub = nip19.npubEncode(pubkey); - const [user, parsed05, followersCount, followingCount, statusesCount] = await Promise.all([ + const [user, parsed05] = await Promise.all([ findUser({ pubkey }), parseAndVerifyNip05(nip05, pubkey), - eventsDB.countFilters([{ kinds: [3], '#p': [pubkey] }]), - getFollowedPubkeys(pubkey).then((pubkeys) => pubkeys.length), - eventsDB.countFilters([{ kinds: [1], authors: [pubkey] }]), ]); return { @@ -40,14 +39,14 @@ async function renderAccount(event: UnsignedEvent<0>, opts: ToAccountOpts = {}) avatar: picture, avatar_static: picture, bot: false, - created_at: event ? nostrDate(event.created_at).toISOString() : new Date().toISOString(), + created_at: nostrDate(event.created_at).toISOString(), discoverable: true, display_name: name, emojis: renderEmojis(event), fields: [], follow_requests_count: 0, - followers_count: followersCount, - following_count: followingCount, + followers_count: event.stats?.followers_count ?? 0, + following_count: event.stats?.following_count ?? 0, fqn: parsed05?.handle || npub, header: banner, header_static: banner, @@ -65,7 +64,7 @@ async function renderAccount(event: UnsignedEvent<0>, opts: ToAccountOpts = {}) follow_requests_count: 0, } : undefined, - statuses_count: statusesCount, + statuses_count: event.stats?.notes_count ?? 0, url: Conf.local(`/users/${pubkey}`), username: parsed05?.nickname || npub.substring(0, 8), pleroma: { diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index 86bdd13..88596d2 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -26,13 +26,10 @@ async function renderStatus(event: eventsDB.DittoEvent<1>, viewerPubkey?: string const { html, links, firstUrl } = parseNoteContent(event.content); - const [mentions, card, repliesCount, reblogsCount, favouritesCount, [repostEvent], [reactionEvent]] = await Promise + const [mentions, card, [repostEvent], [reactionEvent]] = await Promise .all([ Promise.all(mentionedPubkeys.map(toMention)), firstUrl ? unfurlCardCached(firstUrl) : null, - eventsDB.countFilters([{ kinds: [1], '#e': [event.id] }]), - eventsDB.countFilters([{ kinds: [6], '#e': [event.id] }]), - eventsDB.countFilters([{ kinds: [7], '#e': [event.id] }]), viewerPubkey ? eventsDB.getFilters([{ kinds: [6], '#e': [event.id], authors: [viewerPubkey] }], { limit: 1 }) : [], @@ -66,9 +63,9 @@ async function renderStatus(event: eventsDB.DittoEvent<1>, viewerPubkey?: string spoiler_text: (cw ? cw[1] : subject?.[1]) || '', visibility: 'public', language: event.tags.find((tag) => tag[0] === 'lang')?.[1] || null, - replies_count: repliesCount, - reblogs_count: reblogsCount, - favourites_count: favouritesCount, + replies_count: event.stats?.replies_count ?? 0, + reblogs_count: event.stats?.reposts_count ?? 0, + favourites_count: event.stats?.reactions_count ?? 0, favourited: reactionEvent?.content === '+', reblogged: Boolean(repostEvent), muted: false,