Merge branch 'event-relations' into 'main'
Event relations (authors) See merge request soapbox-pub/ditto!70
This commit is contained in:
commit
0724a2744f
|
@ -1,7 +1,8 @@
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { insertUser } from '@/db/users.ts';
|
import { insertUser } from '@/db/users.ts';
|
||||||
import { type Filter, findReplyTag, nip19, z } from '@/deps.ts';
|
import { findReplyTag, nip19, z } from '@/deps.ts';
|
||||||
|
import { type DittoFilter } from '@/filter.ts';
|
||||||
import * as mixer from '@/mixer.ts';
|
import * as mixer from '@/mixer.ts';
|
||||||
import { getAuthor, getFollowedPubkeys, getFollows } from '@/queries.ts';
|
import { getAuthor, getFollowedPubkeys, getFollows } from '@/queries.ts';
|
||||||
import { booleanParamSchema, fileSchema } from '@/schema.ts';
|
import { booleanParamSchema, fileSchema } from '@/schema.ts';
|
||||||
|
@ -137,7 +138,7 @@ const accountStatusesController: AppController = async (c) => {
|
||||||
return c.json([]);
|
return c.json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter: Filter<1> = { authors: [pubkey], kinds: [1], since, until, limit };
|
const filter: DittoFilter<1> = { authors: [pubkey], kinds: [1], relations: ['author'], since, until, limit };
|
||||||
if (tagged) {
|
if (tagged) {
|
||||||
filter['#t'] = [tagged];
|
filter['#t'] = [tagged];
|
||||||
}
|
}
|
||||||
|
@ -256,7 +257,7 @@ 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 }], { timeout: Time.seconds(1) });
|
const events1 = await mixer.getFilters([{ kinds: [1], ids, relations: ['author'] }], { 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);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AppController } from '@/app.ts';
|
import { AppController } from '@/app.ts';
|
||||||
import * as eventsDB from '@/db/events.ts';
|
import * as eventsDB from '@/db/events.ts';
|
||||||
import { type Event, type Filter, nip19, z } from '@/deps.ts';
|
import { type Event, nip19, z } from '@/deps.ts';
|
||||||
|
import { type DittoFilter } from '@/filter.ts';
|
||||||
import * as mixer from '@/mixer.ts';
|
import * as mixer from '@/mixer.ts';
|
||||||
import { booleanParamSchema } from '@/schema.ts';
|
import { booleanParamSchema } from '@/schema.ts';
|
||||||
import { nostrIdSchema } from '@/schemas/nostr.ts';
|
import { nostrIdSchema } from '@/schemas/nostr.ts';
|
||||||
|
@ -65,9 +66,10 @@ const searchController: AppController = async (c) => {
|
||||||
function searchEvents({ q, type, limit, account_id }: SearchQuery): Promise<Event[]> {
|
function searchEvents({ q, type, limit, account_id }: SearchQuery): Promise<Event[]> {
|
||||||
if (type === 'hashtags') return Promise.resolve([]);
|
if (type === 'hashtags') return Promise.resolve([]);
|
||||||
|
|
||||||
const filter: Filter = {
|
const filter: DittoFilter = {
|
||||||
kinds: typeToKinds(type),
|
kinds: typeToKinds(type),
|
||||||
search: q,
|
search: q,
|
||||||
|
relations: ['author'],
|
||||||
limit,
|
limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,8 +100,8 @@ async function lookupEvent(query: SearchQuery): Promise<Event | undefined> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get filters to lookup the input value. */
|
/** Get filters to lookup the input value. */
|
||||||
async function getLookupFilters({ q, type, resolve }: SearchQuery): Promise<Filter[]> {
|
async function getLookupFilters({ q, type, resolve }: SearchQuery): Promise<DittoFilter[]> {
|
||||||
const filters: Filter[] = [];
|
const filters: DittoFilter[] = [];
|
||||||
|
|
||||||
const accounts = !type || type === 'accounts';
|
const accounts = !type || type === 'accounts';
|
||||||
const statuses = !type || type === 'statuses';
|
const statuses = !type || type === 'statuses';
|
||||||
|
@ -138,7 +140,7 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery): Promise<Filt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters;
|
return filters.map((filter) => ({ ...filter, relations: ['author'] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
export { searchController };
|
export { searchController };
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
import { getUnattachedMediaByIds } from '@/db/unattached-media.ts';
|
import { getUnattachedMediaByIds } from '@/db/unattached-media.ts';
|
||||||
import { type Event, ISO6391, z } from '@/deps.ts';
|
import { type Event, ISO6391, z } from '@/deps.ts';
|
||||||
import { getAncestors, getDescendants, getEvent } from '@/queries.ts';
|
import { getAncestors, getAuthor, getDescendants, getEvent } from '@/queries.ts';
|
||||||
import { createEvent, paginationSchema, parseBody } from '@/utils/web.ts';
|
import { createEvent, paginationSchema, parseBody } from '@/utils/web.ts';
|
||||||
import { renderEventAccounts } from '@/views.ts';
|
import { renderEventAccounts } from '@/views.ts';
|
||||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
|
@ -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 });
|
const event = await getEvent(id, { kind: 1, relations: ['author'] });
|
||||||
if (event) {
|
if (event) {
|
||||||
return c.json(await renderStatus(event, c.get('pubkey')));
|
return c.json(await renderStatus(event, c.get('pubkey')));
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,13 @@ const createStatusController: AppController = async (c) => {
|
||||||
tags,
|
tags,
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
return c.json(await renderStatus(event, c.get('pubkey')));
|
const author = await getAuthor(event.pubkey);
|
||||||
|
return c.json(await renderStatus({ ...event, author }, c.get('pubkey')));
|
||||||
};
|
};
|
||||||
|
|
||||||
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 });
|
const event = await getEvent(id, { kind: 1, relations: ['author'] });
|
||||||
|
|
||||||
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'))));
|
||||||
|
@ -109,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 });
|
const target = await getEvent(id, { kind: 1, relations: ['author'] });
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
await createEvent({
|
await createEvent({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
import { z } from '@/deps.ts';
|
import { z } from '@/deps.ts';
|
||||||
import { type DittoFilter } from '@/filter.ts';
|
import { type DittoFilter } from '@/filter.ts';
|
||||||
import { getFeedPubkeys } from '@/queries.ts';
|
import { getAuthor, getFeedPubkeys } from '@/queries.ts';
|
||||||
import { Sub } from '@/subs.ts';
|
import { Sub } from '@/subs.ts';
|
||||||
import { bech32ToPubkey } from '@/utils.ts';
|
import { bech32ToPubkey } from '@/utils.ts';
|
||||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
|
@ -63,7 +63,8 @@ const streamingController: AppController = (c) => {
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
for await (const event of Sub.sub(socket, '1', [filter])) {
|
for await (const event of Sub.sub(socket, '1', [filter])) {
|
||||||
const status = await renderStatus(event, pubkey);
|
const author = await getAuthor(event.pubkey);
|
||||||
|
const status = await renderStatus({ ...event, author }, pubkey);
|
||||||
if (status) {
|
if (status) {
|
||||||
send('update', status);
|
send('update', status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ 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(filters, { timeout: Time.seconds(1) });
|
const events = await mixer.getFilters(
|
||||||
|
filters.map((filter) => ({ ...filter, relations: ['author'] })),
|
||||||
|
{ timeout: Time.seconds(1) },
|
||||||
|
);
|
||||||
|
|
||||||
if (!events.length) {
|
if (!events.length) {
|
||||||
return c.json([]);
|
return c.json([]);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from '@/db.ts';
|
import { db, type DittoDB } from '@/db.ts';
|
||||||
import { type Event } from '@/deps.ts';
|
import { type Event, type SelectQueryBuilder } from '@/deps.ts';
|
||||||
import { isParameterizedReplaceableKind } from '@/kinds.ts';
|
import { isParameterizedReplaceableKind } from '@/kinds.ts';
|
||||||
import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
|
import { jsonMetaContentSchema } from '@/schemas/nostr.ts';
|
||||||
import { EventData } from '@/types.ts';
|
import { EventData } from '@/types.ts';
|
||||||
|
@ -72,8 +72,25 @@ function insertEvent(event: Event, data: EventData): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EventQuery = SelectQueryBuilder<DittoDB, 'events', {
|
||||||
|
id: string;
|
||||||
|
tags: string;
|
||||||
|
kind: number;
|
||||||
|
pubkey: string;
|
||||||
|
content: string;
|
||||||
|
created_at: number;
|
||||||
|
sig: string;
|
||||||
|
author_id?: string;
|
||||||
|
author_tags?: string;
|
||||||
|
author_kind?: number;
|
||||||
|
author_pubkey?: string;
|
||||||
|
author_content?: string;
|
||||||
|
author_created_at?: number;
|
||||||
|
author_sig?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
/** Build the query for a filter. */
|
/** Build the query for a filter. */
|
||||||
function getFilterQuery(filter: DittoFilter) {
|
function getFilterQuery(filter: DittoFilter): EventQuery {
|
||||||
let query = db
|
let query = db
|
||||||
.selectFrom('events')
|
.selectFrom('events')
|
||||||
.select([
|
.select([
|
||||||
|
@ -127,6 +144,30 @@ function getFilterQuery(filter: DittoFilter) {
|
||||||
: query.leftJoin('users', 'users.pubkey', 'events.pubkey').where('users.pubkey', 'is', null) as typeof query;
|
: query.leftJoin('users', 'users.pubkey', 'events.pubkey').where('users.pubkey', 'is', null) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter.relations?.includes('author')) {
|
||||||
|
query = query
|
||||||
|
.leftJoin(
|
||||||
|
(eb) =>
|
||||||
|
eb
|
||||||
|
.selectFrom('events')
|
||||||
|
.selectAll()
|
||||||
|
.where('kind', '=', 0)
|
||||||
|
.orderBy('created_at', 'desc')
|
||||||
|
.groupBy('pubkey')
|
||||||
|
.as('authors'),
|
||||||
|
(join) => join.onRef('authors.pubkey', '=', 'events.pubkey'),
|
||||||
|
)
|
||||||
|
.select([
|
||||||
|
'authors.id as author_id',
|
||||||
|
'authors.kind as author_kind',
|
||||||
|
'authors.pubkey as author_pubkey',
|
||||||
|
'authors.content as author_content',
|
||||||
|
'authors.tags as author_tags',
|
||||||
|
'authors.created_at as author_created_at',
|
||||||
|
'authors.sig as author_sig',
|
||||||
|
]) as typeof query;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter.search) {
|
if (filter.search) {
|
||||||
query = query
|
query = query
|
||||||
.innerJoin('events_fts', 'events_fts.id', 'events.id')
|
.innerJoin('events_fts', 'events_fts.id', 'events.id')
|
||||||
|
@ -143,11 +184,15 @@ function getFiltersQuery(filters: DittoFilter[]) {
|
||||||
.reduce((result, query) => result.unionAll(query));
|
.reduce((result, query) => result.unionAll(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DittoEvent<K extends number = number> extends Event<K> {
|
||||||
|
author?: Event<0>;
|
||||||
|
}
|
||||||
|
|
||||||
/** Get events for filters from the database. */
|
/** Get events for filters from the database. */
|
||||||
async function getFilters<K extends number>(
|
async function getFilters<K extends number>(
|
||||||
filters: DittoFilter<K>[],
|
filters: DittoFilter<K>[],
|
||||||
opts: GetFiltersOpts = {},
|
opts: GetFiltersOpts = {},
|
||||||
): Promise<Event<K>[]> {
|
): Promise<DittoEvent<K>[]> {
|
||||||
if (!filters.length) return Promise.resolve([]);
|
if (!filters.length) return Promise.resolve([]);
|
||||||
let query = getFiltersQuery(filters);
|
let query = getFiltersQuery(filters);
|
||||||
|
|
||||||
|
@ -155,9 +200,31 @@ async function getFilters<K extends number>(
|
||||||
query = query.limit(opts.limit);
|
query = query.limit(opts.limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await query.execute()).map((event) => (
|
return (await query.execute()).map((row) => {
|
||||||
{ ...event, tags: JSON.parse(event.tags) } as Event<K>
|
const event: DittoEvent<K> = {
|
||||||
));
|
id: row.id,
|
||||||
|
kind: row.kind as K,
|
||||||
|
pubkey: row.pubkey,
|
||||||
|
content: row.content,
|
||||||
|
created_at: row.created_at,
|
||||||
|
tags: JSON.parse(row.tags),
|
||||||
|
sig: row.sig,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (row.author_id) {
|
||||||
|
event.author = {
|
||||||
|
id: row.author_id,
|
||||||
|
kind: row.author_kind! as 0,
|
||||||
|
pubkey: row.author_pubkey!,
|
||||||
|
content: row.author_content!,
|
||||||
|
created_at: row.author_created_at!,
|
||||||
|
tags: JSON.parse(row.author_tags!),
|
||||||
|
sig: row.author_sig!,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return event;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete events based on filters from the database. */
|
/** Delete events based on filters from the database. */
|
||||||
|
@ -242,4 +309,4 @@ function buildUserSearchContent(event: Event<0>): string {
|
||||||
return [name, nip05, about].filter(Boolean).join('\n');
|
return [name, nip05, about].filter(Boolean).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
export { countFilters, deleteFilters, getFilters, insertEvent };
|
export { countFilters, deleteFilters, type DittoEvent, getFilters, insertEvent };
|
||||||
|
|
|
@ -67,6 +67,7 @@ export {
|
||||||
Migrator,
|
Migrator,
|
||||||
type NullableInsertKeys,
|
type NullableInsertKeys,
|
||||||
type QueryResult,
|
type QueryResult,
|
||||||
|
type SelectQueryBuilder,
|
||||||
sql,
|
sql,
|
||||||
} from 'npm:kysely@^0.26.3';
|
} from 'npm:kysely@^0.26.3';
|
||||||
export { PolySqliteDialect } from 'https://gitlab.com/soapbox-pub/kysely-deno-sqlite/-/raw/v2.0.0/mod.ts';
|
export { PolySqliteDialect } from 'https://gitlab.com/soapbox-pub/kysely-deno-sqlite/-/raw/v2.0.0/mod.ts';
|
||||||
|
|
|
@ -3,9 +3,15 @@ import { type Event, type Filter, matchFilters } from '@/deps.ts';
|
||||||
|
|
||||||
import type { EventData } from '@/types.ts';
|
import type { EventData } from '@/types.ts';
|
||||||
|
|
||||||
|
/** Additional properties that may be added by Ditto to events. */
|
||||||
|
type Relation = 'author';
|
||||||
|
|
||||||
/** Custom filter interface that extends Nostr filters with extra options for Ditto. */
|
/** Custom filter interface that extends Nostr filters with extra options for Ditto. */
|
||||||
interface DittoFilter<K extends number = number> extends Filter<K> {
|
interface DittoFilter<K extends number = number> extends Filter<K> {
|
||||||
|
/** Whether the event was authored by a local user. */
|
||||||
local?: boolean;
|
local?: boolean;
|
||||||
|
/** Additional fields to add to the returned event. */
|
||||||
|
relations?: Relation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Additional options to apply to the whole subscription. */
|
/** Additional options to apply to the whole subscription. */
|
||||||
|
@ -38,4 +44,4 @@ function matchDittoFilters(filters: DittoFilter[], event: Event, data: EventData
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { type DittoFilter, type GetFiltersOpts, matchDittoFilters };
|
export { type DittoFilter, type GetFiltersOpts, matchDittoFilters, type Relation };
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as eventsDB from '@/db/events.ts';
|
import * as eventsDB from '@/db/events.ts';
|
||||||
import { type Event, type Filter, findReplyTag } from '@/deps.ts';
|
import { type Event, findReplyTag } from '@/deps.ts';
|
||||||
|
import { type DittoFilter, type Relation } from '@/filter.ts';
|
||||||
import * as mixer from '@/mixer.ts';
|
import * as mixer from '@/mixer.ts';
|
||||||
|
|
||||||
interface GetEventOpts<K extends number> {
|
interface GetEventOpts<K extends number> {
|
||||||
|
@ -7,6 +8,8 @@ interface GetEventOpts<K extends number> {
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
/** Event kind. */
|
/** Event kind. */
|
||||||
kind?: K;
|
kind?: K;
|
||||||
|
/** Relations to include on the event. */
|
||||||
|
relations?: Relation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a Nostr event by its ID. */
|
/** Get a Nostr event by its ID. */
|
||||||
|
@ -14,8 +17,8 @@ const getEvent = async <K extends number = number>(
|
||||||
id: string,
|
id: string,
|
||||||
opts: GetEventOpts<K> = {},
|
opts: GetEventOpts<K> = {},
|
||||||
): Promise<Event<K> | undefined> => {
|
): Promise<Event<K> | undefined> => {
|
||||||
const { kind, timeout = 1000 } = opts;
|
const { kind, relations, timeout = 1000 } = opts;
|
||||||
const filter: Filter<K> = { ids: [id], limit: 1 };
|
const filter: DittoFilter<K> = { ids: [id], relations, limit: 1 };
|
||||||
if (kind) {
|
if (kind) {
|
||||||
filter.kinds = [kind];
|
filter.kinds = [kind];
|
||||||
}
|
}
|
||||||
|
@ -57,7 +60,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 });
|
const parentEvent = await getEvent(inReplyTo, { kind: 1, relations: ['author'] });
|
||||||
|
|
||||||
if (parentEvent) {
|
if (parentEvent) {
|
||||||
result.push(parentEvent);
|
result.push(parentEvent);
|
||||||
|
@ -70,7 +73,7 @@ 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] }], { limit: 200, timeout: 2000 });
|
return mixer.getFilters([{ kinds: [1], '#e': [eventId], relations: ['author'] }], { limit: 200, timeout: 2000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the pubkey is followed by a local user. */
|
/** Returns whether the pubkey is followed by a local user. */
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { type Event } from '@/deps.ts';
|
import { type Event } from '@/deps.ts';
|
||||||
|
import { getAuthor } from '@/queries.ts';
|
||||||
import { nostrDate } from '@/utils.ts';
|
import { nostrDate } from '@/utils.ts';
|
||||||
import { accountFromPubkey } from '@/views/mastodon/accounts.ts';
|
import { accountFromPubkey } from '@/views/mastodon/accounts.ts';
|
||||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
|
@ -11,7 +12,8 @@ function renderNotification(event: Event, viewerPubkey?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderNotificationMention(event: Event<1>, viewerPubkey?: string) {
|
async function renderNotificationMention(event: Event<1>, viewerPubkey?: string) {
|
||||||
const status = await renderStatus(event, viewerPubkey);
|
const author = await getAuthor(event.pubkey);
|
||||||
|
const status = await renderStatus({ ...event, author }, viewerPubkey);
|
||||||
if (!status) return;
|
if (!status) return;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ad
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import * as eventsDB from '@/db/events.ts';
|
import * as eventsDB from '@/db/events.ts';
|
||||||
import { type Event, findReplyTag, nip19 } from '@/deps.ts';
|
import { findReplyTag, nip19 } from '@/deps.ts';
|
||||||
import { getMediaLinks, parseNoteContent } from '@/note.ts';
|
import { getMediaLinks, parseNoteContent } from '@/note.ts';
|
||||||
import { getAuthor } from '@/queries.ts';
|
import { getAuthor } from '@/queries.ts';
|
||||||
import { jsonMediaDataSchema } from '@/schemas/nostr.ts';
|
import { jsonMediaDataSchema } from '@/schemas/nostr.ts';
|
||||||
|
@ -12,10 +12,8 @@ import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
|
||||||
import { DittoAttachment, renderAttachment } from '@/views/mastodon/attachments.ts';
|
import { DittoAttachment, renderAttachment } from '@/views/mastodon/attachments.ts';
|
||||||
import { renderEmojis } from '@/views/mastodon/emojis.ts';
|
import { renderEmojis } from '@/views/mastodon/emojis.ts';
|
||||||
|
|
||||||
async function renderStatus(event: Event<1>, viewerPubkey?: string) {
|
async function renderStatus(event: eventsDB.DittoEvent<1>, viewerPubkey?: string) {
|
||||||
const profile = await getAuthor(event.pubkey);
|
const account = event.author ? await renderAccount(event.author) : await accountFromPubkey(event.pubkey);
|
||||||
const account = profile ? await renderAccount(profile) : await accountFromPubkey(event.pubkey);
|
|
||||||
|
|
||||||
const replyTag = findReplyTag(event);
|
const replyTag = findReplyTag(event);
|
||||||
|
|
||||||
const mentionedPubkeys = [
|
const mentionedPubkeys = [
|
||||||
|
|
Loading…
Reference in New Issue