Merge branch 'quotes-count' into 'main'
Quotes count See merge request soapbox-pub/ditto!350
This commit is contained in:
commit
09585214e5
|
@ -62,6 +62,7 @@ import {
|
||||||
favouriteController,
|
favouriteController,
|
||||||
favouritedByController,
|
favouritedByController,
|
||||||
pinController,
|
pinController,
|
||||||
|
quotesController,
|
||||||
rebloggedByController,
|
rebloggedByController,
|
||||||
reblogStatusController,
|
reblogStatusController,
|
||||||
statusController,
|
statusController,
|
||||||
|
@ -189,6 +190,8 @@ app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unreblog', requireSigner, unreblogS
|
||||||
app.post('/api/v1/statuses', requireSigner, createStatusController);
|
app.post('/api/v1/statuses', requireSigner, createStatusController);
|
||||||
app.delete('/api/v1/statuses/:id{[0-9a-f]{64}}', requireSigner, deleteStatusController);
|
app.delete('/api/v1/statuses/:id{[0-9a-f]{64}}', requireSigner, deleteStatusController);
|
||||||
|
|
||||||
|
app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/quotes', quotesController);
|
||||||
|
|
||||||
app.post('/api/v1/media', mediaController);
|
app.post('/api/v1/media', mediaController);
|
||||||
app.post('/api/v2/media', mediaController);
|
app.post('/api/v2/media', mediaController);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { renderEventAccounts } from '@/views.ts';
|
||||||
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { hydrateEvents, purifyEvent } from '@/storages/hydrate.ts';
|
import { hydrateEvents, purifyEvent } from '@/storages/hydrate.ts';
|
||||||
import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts';
|
import { createEvent, paginated, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts';
|
||||||
import { getInvoice, getLnurl } from '@/utils/lnurl.ts';
|
import { getInvoice, getLnurl } from '@/utils/lnurl.ts';
|
||||||
import { lookupPubkey } from '@/utils/lookup.ts';
|
import { lookupPubkey } from '@/utils/lookup.ts';
|
||||||
import { addTag, deleteTag } from '@/utils/tags.ts';
|
import { addTag, deleteTag } from '@/utils/tags.ts';
|
||||||
|
@ -322,6 +322,33 @@ const rebloggedByController: AppController = (c) => {
|
||||||
return renderEventAccounts(c, [{ kinds: [6], '#e': [id], ...params }]);
|
return renderEventAccounts(c, [{ kinds: [6], '#e': [id], ...params }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const quotesController: AppController = async (c) => {
|
||||||
|
const id = c.req.param('id');
|
||||||
|
const params = paginationSchema.parse(c.req.query());
|
||||||
|
const store = await Storages.db();
|
||||||
|
|
||||||
|
const [event] = await store.query([{ ids: [id], kinds: [1] }]);
|
||||||
|
if (!event) {
|
||||||
|
return c.json({ error: 'Event not found.' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotes = await store
|
||||||
|
.query([{ kinds: [1], '#q': [event.id], ...params }])
|
||||||
|
.then((events) => hydrateEvents({ events, store }));
|
||||||
|
|
||||||
|
const viewerPubkey = await c.get('signer')?.getPublicKey();
|
||||||
|
|
||||||
|
const statuses = await Promise.all(
|
||||||
|
quotes.map((event) => renderStatus(event, { viewerPubkey })),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!statuses.length) {
|
||||||
|
return c.json([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return paginated(c, quotes, statuses);
|
||||||
|
};
|
||||||
|
|
||||||
/** https://docs.joinmastodon.org/methods/statuses/#bookmark */
|
/** https://docs.joinmastodon.org/methods/statuses/#bookmark */
|
||||||
const bookmarkController: AppController = async (c) => {
|
const bookmarkController: AppController = async (c) => {
|
||||||
const pubkey = await c.get('signer')?.getPublicKey()!;
|
const pubkey = await c.get('signer')?.getPublicKey()!;
|
||||||
|
@ -487,6 +514,7 @@ export {
|
||||||
favouriteController,
|
favouriteController,
|
||||||
favouritedByController,
|
favouritedByController,
|
||||||
pinController,
|
pinController,
|
||||||
|
quotesController,
|
||||||
rebloggedByController,
|
rebloggedByController,
|
||||||
reblogStatusController,
|
reblogStatusController,
|
||||||
statusController,
|
statusController,
|
||||||
|
|
|
@ -21,6 +21,7 @@ interface EventStatsRow {
|
||||||
replies_count: number;
|
replies_count: number;
|
||||||
reposts_count: number;
|
reposts_count: number;
|
||||||
reactions_count: number;
|
reactions_count: number;
|
||||||
|
quotes_count: number;
|
||||||
reactions: string;
|
reactions: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.alterTable('event_stats')
|
||||||
|
.addColumn('quotes_count', 'integer', (col) => col.notNull().defaultTo(0))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema.alterTable('event_stats').dropColumn('quotes_count').execute();
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ export interface AuthorStats {
|
||||||
export interface EventStats {
|
export interface EventStats {
|
||||||
replies_count: number;
|
replies_count: number;
|
||||||
reposts_count: number;
|
reposts_count: number;
|
||||||
|
quotes_count: number;
|
||||||
reactions: Record<string, number>;
|
reactions: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,7 @@ async function gatherEventStats(events: DittoEvent[]): Promise<DittoTables['even
|
||||||
reposts_count: Math.max(0, row.reposts_count),
|
reposts_count: Math.max(0, row.reposts_count),
|
||||||
replies_count: Math.max(0, row.replies_count),
|
replies_count: Math.max(0, row.replies_count),
|
||||||
reactions_count: Math.max(0, row.reactions_count),
|
reactions_count: Math.max(0, row.reactions_count),
|
||||||
|
quotes_count: Math.max(0, row.quotes_count),
|
||||||
reactions: row.reactions,
|
reactions: row.reactions,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Kysely, UpdateObject } from 'kysely';
|
||||||
import { SetRequired } from 'type-fest';
|
import { SetRequired } from 'type-fest';
|
||||||
|
|
||||||
import { DittoTables } from '@/db/DittoTables.ts';
|
import { DittoTables } from '@/db/DittoTables.ts';
|
||||||
import { findReplyTag, getTagSet } from '@/utils/tags.ts';
|
import { findQuoteTag, findReplyTag, getTagSet } from '@/utils/tags.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
|
||||||
interface UpdateStatsOpts {
|
interface UpdateStatsOpts {
|
||||||
|
@ -34,14 +34,24 @@ export async function updateStats({ event, kysely, store, x = 1 }: UpdateStatsOp
|
||||||
async function handleEvent1(kysely: Kysely<DittoTables>, event: NostrEvent, x: number): Promise<void> {
|
async function handleEvent1(kysely: Kysely<DittoTables>, event: NostrEvent, x: number): Promise<void> {
|
||||||
await updateAuthorStats(kysely, event.pubkey, ({ notes_count }) => ({ notes_count: Math.max(0, notes_count + x) }));
|
await updateAuthorStats(kysely, event.pubkey, ({ notes_count }) => ({ notes_count: Math.max(0, notes_count + x) }));
|
||||||
|
|
||||||
const inReplyToId = findReplyTag(event.tags)?.[1];
|
const replyId = findReplyTag(event.tags)?.[1];
|
||||||
if (inReplyToId) {
|
const quoteId = findQuoteTag(event.tags)?.[1];
|
||||||
|
|
||||||
|
if (replyId) {
|
||||||
await updateEventStats(
|
await updateEventStats(
|
||||||
kysely,
|
kysely,
|
||||||
inReplyToId,
|
replyId,
|
||||||
({ replies_count }) => ({ replies_count: Math.max(0, replies_count + x) }),
|
({ replies_count }) => ({ replies_count: Math.max(0, replies_count + x) }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quoteId) {
|
||||||
|
await updateEventStats(
|
||||||
|
kysely,
|
||||||
|
quoteId,
|
||||||
|
({ quotes_count }) => ({ quotes_count: Math.max(0, quotes_count + x) }),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update stats for kind 3 event. */
|
/** Update stats for kind 3 event. */
|
||||||
|
@ -208,6 +218,7 @@ export async function updateEventStats(
|
||||||
replies_count: 0,
|
replies_count: 0,
|
||||||
reposts_count: 0,
|
reposts_count: 0,
|
||||||
reactions_count: 0,
|
reactions_count: 0,
|
||||||
|
quotes_count: 0,
|
||||||
reactions: '{}',
|
reactions: '{}',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<
|
||||||
pleroma: {
|
pleroma: {
|
||||||
emoji_reactions: reactions,
|
emoji_reactions: reactions,
|
||||||
expires_at: !isNaN(expiresAt.getTime()) ? expiresAt.toISOString() : undefined,
|
expires_at: !isNaN(expiresAt.getTime()) ? expiresAt.toISOString() : undefined,
|
||||||
|
quotes_count: event.event_stats?.quotes_count ?? 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue