pl-api: support mastodon quotes

Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
Nicole Mikołajczyk
2025-05-28 12:17:24 +02:00
parent 4c905b6112
commit 88dabae10d
7 changed files with 75 additions and 14 deletions

View File

@ -0,0 +1,33 @@
import * as v from 'valibot';
import { statusSchema } from './status';
/**
* @category Schemas
* @see {@link https://docs.joinmastodon.org/entities/Quote/}
*/
const quoteSchema = v.object({
state: v.fallback(v.picklist(['pending', 'accepted', 'rejected', 'revoked', 'deleted', 'unauthorized']), 'accepted'),
status: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
});
/**
* @category Entity types
*/
type Quote = v.InferOutput<typeof quoteSchema>;
/**
* @category Schemas
* @see {@link https://docs.joinmastodon.org/entities/ShallowQuote/}
*/
const shallowQuoteSchema = v.object({
state: v.fallback(v.picklist(['pending', 'accepted', 'rejected', 'revoked', 'deleted', 'unauthorized']), 'accepted'),
status_id: v.fallback(v.nullable(v.string()), null),
});
/**
* @category Entity types
*/
type ShallowQuote = v.InferOutput<typeof shallowQuoteSchema>;
export { quoteSchema, shallowQuoteSchema, type Quote, type ShallowQuote };

View File

@ -11,6 +11,7 @@ import { mediaAttachmentSchema } from './media-attachment';
import { mentionSchema } from './mention';
import { pollSchema } from './poll';
import { previewCardSchema } from './preview-card';
import { type Quote, quoteSchema, type ShallowQuote, shallowQuoteSchema } from './quote';
import { rssFeedSchema } from './rss-feed';
import { tagSchema } from './tag';
import { translationSchema } from './translation';
@ -109,12 +110,37 @@ const baseStatusSchema = v.object({
const preprocess = (status: any) => {
if (!status) return null;
let quote: {
state: string;
status: any;
} | {
state: string;
status_id: string;
} | null = null;
const quotedStatus = status.quote ?? status.pleroma?.quote;
const quotedStatusId = quotedStatus?.id ?? status.quote_id ?? status.pleroma?.quote_id;
if (quotedStatus?.state) {
quote = quotedStatus;
} else if (quotedStatus) {
quote = {
state: 'accepted',
status: quotedStatus,
};
} else {
if (quotedStatusId) {
quote = {
state: 'accepted',
status_id: quotedStatusId,
};
}
}
status = {
// @ts-ignore
emoji_reactions: status.reactions,
...(pick(status.pleroma || {}, [
'quote',
'quote_id',
'local',
'conversation_id',
'direct_conversation_id',
@ -137,6 +163,8 @@ const preprocess = (status: any) => {
'disliked',
])),
...status,
quote,
quote_id: quotedStatusId,
};
if (!status.interaction_policy && status.comments_disabled === true) {
@ -168,13 +196,13 @@ const statusWithoutAccountSchema = v.pipe(v.any(), v.transform(preprocess), v.ob
account: v.fallback(v.nullable(accountSchema), null),
reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
quote: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null),
}));
const partialStatusSchema = v.partial(v.object({
...baseStatusSchema.entries,
reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
quote: v.fallback(v.nullable(v.lazy(() => statusSchema)), null),
quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null),
}));
/**
@ -183,7 +211,7 @@ const partialStatusSchema = v.partial(v.object({
type StatusWithoutAccount = Omit<v.InferOutput<typeof baseStatusSchema>, 'account'> & {
account: Account | null;
reblog: Status | null;
quote: Status | null;
quote: Quote | ShallowQuote | null;
}
/**
@ -191,7 +219,7 @@ type StatusWithoutAccount = Omit<v.InferOutput<typeof baseStatusSchema>, 'accoun
*/
type Status = v.InferOutput<typeof baseStatusSchema> & {
reblog: Status | null;
quote: Status | null;
quote: Quote | ShallowQuote | null;
}
export { statusSchema, statusWithoutAccountSchema, partialStatusSchema, type Status, type StatusWithoutAccount };

View File

@ -1,6 +1,6 @@
{
"name": "pl-api",
"version": "1.0.0-rc.69",
"version": "1.0.0-rc.70",
"type": "module",
"homepage": "https://github.com/mkljczk/pl-fe/tree/develop/packages/pl-api",
"repository": {

View File

@ -105,7 +105,7 @@
"multiselect-react-dropdown": "^2.0.25",
"mutative": "^1.1.0",
"path-browserify": "^1.0.1",
"pl-api": "^1.0.0-rc.69",
"pl-api": "^1.0.0-rc.70",
"postcss": "^8.5.3",
"process": "^0.11.10",
"punycode": "^2.1.1",

View File

@ -79,7 +79,7 @@ const importEntities = (entities: {
processAccount(status.account);
}
if (status.quote) processStatus(status.quote);
if (status.quote && 'status' in status.quote && status.quote.status) processStatus(status.quote.status);
if (status.reblog) processStatus(status.reblog);
if (status.poll) polls[status.poll.id] = status.poll;
if (status.group) groups[status.group.id] = status.group;

View File

@ -117,7 +117,7 @@ const normalizeStatus = (status: BaseStatus & {
showFiltered: null as null | boolean,
deleted: false,
...status,
quote_id: status.quote?.id || status.quote_id || null,
quote_id: status.quote_id || null,
account: normalizeAccount(status.account),
accounts: status.accounts?.map(normalizeAccount),
mentions,

View File

@ -6875,10 +6875,10 @@ pkg-dir@^4.1.0:
dependencies:
find-up "^4.0.0"
pl-api@^1.0.0-rc.69:
version "1.0.0-rc.69"
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.69.tgz#a271fcd8963e1df106bee0e081b3d4f728094259"
integrity sha512-9EVEiOE5hNBLac+0C+Z/daAxhi0IRjR0arY1m9NIvnvUfUVIbJNSSWTdVtFyd2e0CqOhFG/fiCxNZRR/qqyuNg==
pl-api@^1.0.0-rc.70:
version "1.0.0-rc.70"
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.70.tgz#0f140b780fad2628746b021a0f8a384b1eb7323c"
integrity sha512-QISMfayJOFE1+JzhKBdOKRpzByiLre20MHDss9WwWdQIPGSO1RRoDVb7ufTx2ER6ZeX+9zXzG7VPfh3gMhafQQ==
dependencies:
blurhash "^2.0.5"
http-link-header "^1.1.3"