Support reactions on Mastodon forks, support api_versions, minor fixes
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
0a7bdbb7d0
commit
8794c293aa
14 changed files with 220 additions and 145 deletions
|
@ -217,7 +217,7 @@ class PlApiClient {
|
|||
#accessToken?: string;
|
||||
#instance: Instance = instanceSchema.parse({});
|
||||
public request = request.bind(this) as typeof request;
|
||||
public features: Features = getFeatures();
|
||||
public features: Features = getFeatures(this.#instance);
|
||||
#socket?: {
|
||||
listen: (listener: any, stream?: string) => number;
|
||||
unlisten: (listener: any) => void;
|
||||
|
@ -2015,14 +2015,34 @@ class PlApiClient {
|
|||
/**
|
||||
* Get an object of emoji to account mappings with accounts that reacted to the post
|
||||
*
|
||||
* Requires features{@link Features['emojiReacts']}.
|
||||
* Requires features{@link Features['emojiReactsList']}.
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactions}
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactionsemoji}
|
||||
*/
|
||||
getStatusReactions: async (statusId: string, emoji?: string) => {
|
||||
const response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions${emoji ? `/${emoji}` : ''}`);
|
||||
const apiVersions = this.#instance.api_versions;
|
||||
|
||||
return filteredArray(emojiReactionSchema).parse(response.json);
|
||||
let response;
|
||||
if (apiVersions['pleroma_emoji_reactions.pleroma.pl-api'] >= 1) {
|
||||
response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions${emoji ? `/${emoji}` : ''}`);
|
||||
} else if (apiVersions['emoji_reaction.fedibird.pl-api'] >= 1) {
|
||||
response = await this.request(`/api/v1/statuses/${statusId}/emoji_reactioned_by`);
|
||||
response.json = response.json?.reduce((acc: Array<any>, cur: any) => {
|
||||
if (emoji && cur.name !== emoji) return acc;
|
||||
|
||||
const existing = acc.find(reaction => reaction.name === cur.name);
|
||||
|
||||
if (existing) {
|
||||
existing.accounts.push(cur.account);
|
||||
existing.account_ids.push(cur.account.id);
|
||||
existing.count += 1;
|
||||
} else acc.push({ count: 1, accounts: [cur.account], account_ids: [cur.account.id], ...cur });
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return filteredArray(emojiReactionSchema).parse(response?.json || []);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2033,7 +2053,14 @@ class PlApiClient {
|
|||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#put-apiv1pleromastatusesidreactionsemoji}
|
||||
*/
|
||||
createStatusReaction: async (statusId: string, emoji: string) => {
|
||||
const response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, { method: 'PUT' });
|
||||
const apiVersions = this.#instance.api_versions;
|
||||
|
||||
let response;
|
||||
if (apiVersions['pleroma_emoji_reactions.pleroma.pl-api'] >= 1) {
|
||||
response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${encodeURIComponent(emoji)}`, { method: 'PUT' });
|
||||
} else {
|
||||
response = await this.request(`/api/v1/statuses/${statusId}/react/${encodeURIComponent(emoji)}`, { method: 'POST' });
|
||||
}
|
||||
|
||||
return statusSchema.parse(response.json);
|
||||
},
|
||||
|
@ -2045,7 +2072,14 @@ class PlApiClient {
|
|||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#delete-apiv1pleromastatusesidreactionsemoji}
|
||||
*/
|
||||
deleteStatusReaction: async (statusId: string, emoji: string) => {
|
||||
const response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, { method: 'DELETE' });
|
||||
const apiVersions = this.#instance.api_versions;
|
||||
|
||||
let response;
|
||||
if (apiVersions['pleroma_emoji_reactions.pleroma.pl-api'] >= 1) {
|
||||
response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, { method: 'DELETE' });
|
||||
} else {
|
||||
response = await this.request(`/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(emoji)}`, { method: 'POST' });
|
||||
}
|
||||
|
||||
return statusSchema.parse(response.json);
|
||||
},
|
||||
|
|
|
@ -12,17 +12,12 @@ const filterBadges = (tags?: string[]) =>
|
|||
const preprocessAccount = (account: any) => {
|
||||
if (!account?.acct) return null;
|
||||
|
||||
const username = account.username || account.acct.split('@')[0];
|
||||
|
||||
return {
|
||||
username: account.username || account.acct.split('@')[0],
|
||||
display_name: account.display_name.trim() || account.username,
|
||||
roles: account.roles?.length ? account.roles : filterBadges(account.pleroma?.tags),
|
||||
username,
|
||||
avatar_static: account.avatar_static || account.avatar,
|
||||
header_static: account.header_static || account.header,
|
||||
source: account.source
|
||||
? { ...(pick(account.pleroma?.source || {}, [
|
||||
'show_role', 'no_rich_text', 'discoverable', 'actor_type', 'show_birthday',
|
||||
])), ...account.source }
|
||||
: undefined,
|
||||
local: typeof account.pleroma?.is_local === 'boolean' ? account.pleroma.is_local : account.acct.split('@')[1] === undefined,
|
||||
discoverable: account.discoverable || account.pleroma?.source?.discoverable,
|
||||
verified: account.verified || account.pleroma?.tags?.includes('verified'),
|
||||
|
@ -54,6 +49,13 @@ const preprocessAccount = (account: any) => {
|
|||
...(pick(account.other_settings || {}), ['birthday', 'location']),
|
||||
__meta: pick(account, ['pleroma', 'source']),
|
||||
...account,
|
||||
display_name: account.display_name.trim() || username,
|
||||
roles: account.roles?.length ? account.roles : filterBadges(account.pleroma?.tags),
|
||||
source: account.source
|
||||
? { ...(pick(account.pleroma?.source || {}, [
|
||||
'show_role', 'no_rich_text', 'discoverable', 'actor_type', 'show_birthday',
|
||||
])), ...account.source }
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -8,19 +8,26 @@ const baseEmojiReactionSchema = z.object({
|
|||
me: z.boolean().catch(false),
|
||||
name: emojiSchema,
|
||||
url: z.literal(undefined).catch(undefined),
|
||||
static_url: z.literal(undefined).catch(undefined),
|
||||
accounts: filteredArray(accountSchema),
|
||||
account_ids: filteredArray(z.string()).catch([]),
|
||||
});
|
||||
|
||||
const customEmojiReactionSchema = baseEmojiReactionSchema.extend({
|
||||
name: z.string(),
|
||||
url: z.string().url(),
|
||||
static_url: z.string().url(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Pleroma emoji reaction.
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/differences_in_mastoapi_responses/#statuses}
|
||||
*/
|
||||
const emojiReactionSchema = baseEmojiReactionSchema.or(customEmojiReactionSchema);
|
||||
const emojiReactionSchema = z.preprocess((reaction: any) => reaction ? {
|
||||
static_url: reaction.url,
|
||||
account_ids: reaction.accounts?.map((account: any) => account?.id),
|
||||
...reaction,
|
||||
} : null, baseEmojiReactionSchema.or(customEmojiReactionSchema));
|
||||
|
||||
type EmojiReaction = z.infer<typeof emojiReactionSchema>;
|
||||
|
||||
|
|
|
@ -5,6 +5,83 @@ import { accountSchema } from './account';
|
|||
import { ruleSchema } from './rule';
|
||||
import { coerceObject, filteredArray, mimeSchema } from './utils';
|
||||
|
||||
const getApiVersions = (instance: any) => ({
|
||||
...Object.fromEntries(instance.pleroma?.metadata?.features?.map((feature: string) => {
|
||||
let string = `${feature}.pleroma.pl-api`;
|
||||
if (string.startsWith('pleroma:') || string.startsWith('pleroma_')) string = string.slice(8);
|
||||
if (string.startsWith('akkoma:')) string = string.slice(7);
|
||||
if (string.startsWith('pl:')) string = string.slice(3);
|
||||
return [string, 1];
|
||||
}) || []),
|
||||
...Object.fromEntries(instance.fedibird_capabilities?.map((feature: string) => [`${feature}.fedibird.pl-api`, 1]) || []),
|
||||
...instance.api_versions,
|
||||
});
|
||||
|
||||
const instanceV1ToV2 = (data: any) => {
|
||||
const {
|
||||
approval_required,
|
||||
configuration,
|
||||
contact_account,
|
||||
description,
|
||||
description_limit,
|
||||
email,
|
||||
max_media_attachments,
|
||||
max_toot_chars,
|
||||
poll_limits,
|
||||
pleroma,
|
||||
registrations,
|
||||
short_description,
|
||||
thumbnail,
|
||||
uri,
|
||||
urls,
|
||||
...instance
|
||||
} = instanceV1Schema.parse(data);
|
||||
|
||||
return {
|
||||
...instance,
|
||||
account_domain: instance.account_domain || uri,
|
||||
configuration: {
|
||||
...configuration,
|
||||
polls: {
|
||||
...configuration.polls,
|
||||
max_characters_per_option: configuration.polls.max_characters_per_option ?? poll_limits.max_option_chars ?? 25,
|
||||
max_expiration: configuration.polls.max_expiration ?? poll_limits.max_expiration ?? 2629746,
|
||||
max_options: configuration.polls.max_options ?? poll_limits.max_options ?? 4,
|
||||
min_expiration: configuration.polls.min_expiration ?? poll_limits.min_expiration ?? 300,
|
||||
},
|
||||
statuses: {
|
||||
...configuration.statuses,
|
||||
max_characters: configuration.statuses.max_characters ?? max_toot_chars ?? 500,
|
||||
max_media_attachments: configuration.statuses.max_media_attachments ?? max_media_attachments,
|
||||
},
|
||||
urls: {
|
||||
streaming: urls.streaming_api,
|
||||
},
|
||||
vapid: {
|
||||
public_key: pleroma.vapid_public_key,
|
||||
},
|
||||
},
|
||||
contact: {
|
||||
account: contact_account,
|
||||
email: email,
|
||||
},
|
||||
description: short_description || description,
|
||||
domain: uri,
|
||||
pleroma: {
|
||||
...pleroma,
|
||||
metadata: {
|
||||
...pleroma.metadata,
|
||||
description_limit,
|
||||
},
|
||||
},
|
||||
registrations: {
|
||||
approval_required: approval_required,
|
||||
enabled: registrations,
|
||||
},
|
||||
thumbnail: { url: thumbnail },
|
||||
};
|
||||
};
|
||||
|
||||
const fixVersion = (version: string) => {
|
||||
// Handle Mastodon release candidates
|
||||
if (new RegExp(/[0-9.]+rc[0-9]+/g).test(version)) {
|
||||
|
@ -47,18 +124,18 @@ const configurationSchema = coerceObject({
|
|||
video_size_limit: z.number().optional().catch(undefined),
|
||||
}),
|
||||
polls: coerceObject({
|
||||
max_characters_per_option: z.number().optional().catch(undefined),
|
||||
max_expiration: z.number().optional().catch(undefined),
|
||||
max_options: z.number().optional().catch(undefined),
|
||||
min_expiration: z.number().optional().catch(undefined),
|
||||
max_characters_per_option: z.number().catch(25),
|
||||
max_expiration: z.number().catch(2629746),
|
||||
max_options: z.number().catch(4),
|
||||
min_expiration: z.number().catch(300),
|
||||
}),
|
||||
reactions: coerceObject({
|
||||
max_reactions: z.number().catch(0),
|
||||
}),
|
||||
statuses: coerceObject({
|
||||
characters_reserved_per_url: z.number().optional().catch(undefined),
|
||||
max_characters: z.number().optional().catch(undefined),
|
||||
max_media_attachments: z.number().optional().catch(undefined),
|
||||
max_characters: z.number().catch(500),
|
||||
max_media_attachments: z.number().catch(4),
|
||||
|
||||
}),
|
||||
translation: coerceObject({
|
||||
|
@ -67,6 +144,9 @@ const configurationSchema = coerceObject({
|
|||
urls: coerceObject({
|
||||
streaming: z.string().url().optional().catch(undefined),
|
||||
}),
|
||||
vapid: coerceObject({
|
||||
public_key: z.string().catch(''),
|
||||
}),
|
||||
});
|
||||
|
||||
const contactSchema = coerceObject({
|
||||
|
@ -214,69 +294,14 @@ const instanceSchema = z.preprocess((data: any) => {
|
|||
data.version = `0.0.0 (compatible; GoToSocial ${data.version})`;
|
||||
}
|
||||
|
||||
if (data.domain) return { account_domain: data.domain, ...data };
|
||||
const apiVersions = getApiVersions(data);
|
||||
|
||||
const {
|
||||
approval_required,
|
||||
configuration,
|
||||
contact_account,
|
||||
description,
|
||||
description_limit,
|
||||
email,
|
||||
max_media_attachments,
|
||||
max_toot_chars,
|
||||
poll_limits,
|
||||
pleroma,
|
||||
registrations,
|
||||
short_description,
|
||||
thumbnail,
|
||||
uri,
|
||||
urls,
|
||||
...instance
|
||||
} = instanceV1Schema.parse(data);
|
||||
if (data.domain) return { account_domain: data.domain, ...data, api_versions: apiVersions };
|
||||
|
||||
return {
|
||||
...instance,
|
||||
account_domain: instance.account_domain || uri,
|
||||
configuration: {
|
||||
...configuration,
|
||||
polls: {
|
||||
...configuration.polls,
|
||||
max_characters_per_option: configuration.polls.max_characters_per_option ?? poll_limits.max_option_chars ?? 25,
|
||||
max_expiration: configuration.polls.max_expiration ?? poll_limits.max_expiration ?? 2629746,
|
||||
max_options: configuration.polls.max_options ?? poll_limits.max_options ?? 4,
|
||||
min_expiration: configuration.polls.min_expiration ?? poll_limits.min_expiration ?? 300,
|
||||
},
|
||||
statuses: {
|
||||
...configuration.statuses,
|
||||
max_characters: configuration.statuses.max_characters ?? max_toot_chars ?? 500,
|
||||
max_media_attachments: configuration.statuses.max_media_attachments ?? max_media_attachments,
|
||||
},
|
||||
urls: {
|
||||
streaming: urls.streaming_api,
|
||||
},
|
||||
},
|
||||
contact: {
|
||||
account: contact_account,
|
||||
email: email,
|
||||
},
|
||||
description: short_description || description,
|
||||
domain: uri,
|
||||
pleroma: {
|
||||
...pleroma,
|
||||
metadata: {
|
||||
...pleroma.metadata,
|
||||
description_limit,
|
||||
},
|
||||
},
|
||||
registrations: {
|
||||
approval_required: approval_required,
|
||||
enabled: registrations,
|
||||
},
|
||||
thumbnail: { url: thumbnail },
|
||||
};
|
||||
return instanceV1ToV2({ api_versions: apiVersions, ...data });
|
||||
}, coerceObject({
|
||||
account_domain: z.string().catch(''),
|
||||
api_versions: z.record(z.number()).catch({}),
|
||||
configuration: configurationSchema,
|
||||
contact: contactSchema,
|
||||
description: z.string().catch(''),
|
||||
|
@ -292,30 +317,11 @@ const instanceSchema = z.preprocess((data: any) => {
|
|||
title: z.string().catch(''),
|
||||
usage: usageSchema,
|
||||
version: z.string().catch('0.0.0'),
|
||||
}).transform(({ configuration, ...instance }) => {
|
||||
}).transform((instance) => {
|
||||
const version = fixVersion(instance.version);
|
||||
|
||||
const polls = {
|
||||
...configuration.polls,
|
||||
max_characters_per_option: configuration.polls.max_characters_per_option ?? 25,
|
||||
max_expiration: configuration.polls.max_expiration ?? 2629746,
|
||||
max_options: configuration.polls.max_options ?? 4,
|
||||
min_expiration: configuration.polls.min_expiration ?? 300,
|
||||
};
|
||||
|
||||
const statuses = {
|
||||
...configuration.statuses,
|
||||
max_characters: configuration.statuses.max_characters ?? 500,
|
||||
max_media_attachments: configuration.statuses.max_media_attachments ?? 4,
|
||||
};
|
||||
|
||||
return {
|
||||
...instance,
|
||||
configuration: {
|
||||
...configuration,
|
||||
polls,
|
||||
statuses,
|
||||
},
|
||||
version,
|
||||
};
|
||||
}));
|
||||
|
|
|
@ -13,6 +13,7 @@ const baseNotificationSchema = z.object({
|
|||
account: accountSchema,
|
||||
created_at: dateSchema,
|
||||
id: z.string(),
|
||||
group_key: z.string(),
|
||||
type: z.string(),
|
||||
|
||||
is_muted: z.boolean().optional().catch(undefined),
|
||||
|
@ -68,6 +69,7 @@ const eventParticipationRequestNotificationSchema = baseNotificationSchema.exten
|
|||
|
||||
/** @see {@link https://docs.joinmastodon.org/entities/Notification/} */
|
||||
const notificationSchema: z.ZodType<Notification> = z.preprocess((notification: any) => ({
|
||||
group_key: `ungrouped-${notification.id}`,
|
||||
...pick(notification.pleroma || {}, ['is_muted', 'is_seen']),
|
||||
...notification,
|
||||
type: notification.type === 'pleroma:report'
|
||||
|
|
|
@ -101,10 +101,9 @@ const REBASED = 'soapbox';
|
|||
const UNRELEASED = 'unreleased';
|
||||
|
||||
/** Parse features for the given instance */
|
||||
const getFeatures = (instance?: Instance) => {
|
||||
const v = parseVersion(instance?.version || '');
|
||||
const features = instance?.pleroma.metadata.features || [];
|
||||
const federation = !!instance?.pleroma.metadata.federation.enabled;
|
||||
const getFeatures = (instance: Instance) => {
|
||||
const v = parseVersion(instance.version || '');
|
||||
const federation = !!instance.pleroma.metadata.federation.enabled;
|
||||
|
||||
return {
|
||||
version: v,
|
||||
|
@ -231,11 +230,11 @@ const getFeatures = (instance?: Instance) => {
|
|||
*/
|
||||
bites: any([
|
||||
v.software === TOKI,
|
||||
features.includes('pleroma:bites'),
|
||||
instance.api_versions['bites.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/** Whether people who blocked you are visible through the API. */
|
||||
blockersVisible: features.includes('blockers_visible'),
|
||||
blockersVisible: instance.api_versions['blockers_visible.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Can group bookmarks in folders.
|
||||
|
@ -244,7 +243,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
* @see PATCH /api/v1/pleroma/bookmark_folders/:id
|
||||
* @see DELETE /api/v1/pleroma/bookmark_folders/:id
|
||||
*/
|
||||
bookmarkFolders: features.includes('pleroma:bookmark_folders'),
|
||||
bookmarkFolders: instance.api_versions['bookmark_folders.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Can bookmark statuses.
|
||||
|
@ -278,13 +277,13 @@ const getFeatures = (instance?: Instance) => {
|
|||
* Can display a timeline of statuses from instances selected by instance admin.
|
||||
* @see GET /api/v1/timelines/bubble
|
||||
*/
|
||||
bubbleTimeline: features.includes('bubble_timeline'),
|
||||
bubbleTimeline: instance.api_versions['bubble_timeline.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Pleroma chats API.
|
||||
* @see {@link https://docs.pleroma.social/backend/development/API/chats/}
|
||||
*/
|
||||
chats: features.includes('pleroma_chat_messages'),
|
||||
chats: instance.api_versions['chat_messages.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Ability to delete a chat.
|
||||
|
@ -353,8 +352,8 @@ const getFeatures = (instance?: Instance) => {
|
|||
* Ability to add non-standard reactions to a status.
|
||||
*/
|
||||
customEmojiReacts: any([
|
||||
features.includes('pleroma_custom_emoji_reactions'),
|
||||
features.includes('custom_emoji_reactions'),
|
||||
instance.api_versions['custom_emoji_reactions.pleroma.pl-api'] >= 1,
|
||||
instance.api_versions['custom_emoji_reactions.pleroma.pl-api'] >= 1,
|
||||
v.software === PLEROMA && gte(v.version, '2.6.0'),
|
||||
]),
|
||||
|
||||
|
@ -403,7 +402,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
v.software === MASTODON,
|
||||
v.software === MITRA,
|
||||
v.software === TAKAHE && gte(v.version, '0.8.0'),
|
||||
features.includes('editing'),
|
||||
instance.api_versions['editing.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
@ -414,7 +413,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
* @see GET /api/v1/pleroma/admin/email_list/unsubscribers.csv
|
||||
* @see GET /api/v1/pleroma/admin/email_list/combined.csv
|
||||
*/
|
||||
emailList: features.includes('email_list'),
|
||||
emailList: instance.api_versions['email_list.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Ability to embed posts on external sites.
|
||||
|
@ -425,17 +424,25 @@ const getFeatures = (instance?: Instance) => {
|
|||
/**
|
||||
* Ability to add emoji reactions to a status.
|
||||
* @see PUT /api/v1/pleroma/statuses/:id/reactions/:emoji
|
||||
* @see GET /api/v1/pleroma/statuses/:id/reactions/:emoji?
|
||||
* @see DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji
|
||||
*/
|
||||
emojiReacts: v.software === PLEROMA,
|
||||
|
||||
/**
|
||||
* Ability to add emoji reactions to a status available in Mastodon forks.
|
||||
*
|
||||
* @see POST /v1/statuses/:id/react/:emoji
|
||||
* @see POST /v1/statuses/:id/unreact/:emoji
|
||||
*/
|
||||
emojiReactsMastodon: instance ? instance.configuration.reactions.max_reactions > 0 : false,
|
||||
emojiReacts: any([
|
||||
v.software === PLEROMA,
|
||||
instance ? instance.configuration.reactions.max_reactions > 0 : false,
|
||||
]),
|
||||
|
||||
/**
|
||||
* @see GET /api/v1/pleroma/statuses/:id/reactions/:emoji?
|
||||
*
|
||||
* @see GET /api/v1/statuses/:id/emoji_reactioned_by
|
||||
*/
|
||||
emojiReactsList: any([
|
||||
v.software === PLEROMA,
|
||||
instance.api_versions['emoji_reaction.fedibird.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
* Ability to create and perform actions on events.
|
||||
|
@ -451,7 +458,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
* @see GET /api/v1/pleroma/events/:id/ics
|
||||
* @see GET /api/v1/pleroma/search/location
|
||||
*/
|
||||
events: features.includes('events'),
|
||||
events: instance.api_versions['events.pleroma.pl-api'] >= 1,
|
||||
|
||||
/** Whether to allow exporting follows/blocks/mutes to CSV by paginating the API. */
|
||||
exportData: true,
|
||||
|
@ -465,7 +472,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
v.software === MASTODON,
|
||||
v.software === TAKAHE && gte(v.version, '0.6.1'),
|
||||
v.software === TOKI,
|
||||
features.includes('exposable_reactions'),
|
||||
instance.api_versions['exposable_reactions.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
@ -582,7 +589,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
* @see POST /api/v1/admin/groups/:group_id/unsuspend
|
||||
* @see DELETE /api/v1/admin/groups/:group_id
|
||||
*/
|
||||
groups: features.includes('pleroma:groups'),
|
||||
groups: instance.api_versions['pleroma:groups.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Can hide follows/followers lists and counts.
|
||||
|
@ -649,13 +656,13 @@ const getFeatures = (instance?: Instance) => {
|
|||
/**
|
||||
* Server-side status language detection.
|
||||
*/
|
||||
languageDetection: features.includes('pleroma:language_detection'),
|
||||
languageDetection: instance.api_versions['language_detection.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Can translate multiple statuses in a single request.
|
||||
* @see POST /api/v1/pl/statuses/translate
|
||||
*/
|
||||
lazyTranslations: features.includes('pl:translations'),
|
||||
lazyTranslations: instance.api_versions['translations.pl.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Can create, view, and manage lists.
|
||||
|
@ -744,7 +751,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
* Ability to include multiple language variants for a post.
|
||||
* @see POST /api/v1/statuses
|
||||
*/
|
||||
multiLanguage: features.includes('pleroma:multi_language'),
|
||||
multiLanguage: instance.api_versions['multi_language.pleroma.pl-api'] >= 1,
|
||||
|
||||
/**
|
||||
* Ability to hide notifications from people you don't follow.
|
||||
|
@ -898,7 +905,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
v.software === FRIENDICA,
|
||||
v.software === MASTODON,
|
||||
v.software === MITRA,
|
||||
features.includes('profile_directory'),
|
||||
instance.api_versions['profile_directory.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
@ -943,7 +950,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
quotePosts: any([
|
||||
v.software === FRIENDICA && gte(v.version, '2023.3.0'),
|
||||
v.software === PLEROMA && [REBASED, AKKOMA].includes(v.build!) && gte(v.version, '2.5.0'),
|
||||
features.includes('quote_posting'),
|
||||
instance.api_versions['quote_posting.pleroma.pl-api'] >= 1,
|
||||
instance?.feature_quote === true,
|
||||
]),
|
||||
|
||||
|
@ -1082,7 +1089,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
v.software === FRIENDICA,
|
||||
v.software === ICESHRIMP,
|
||||
v.software === MASTODON,
|
||||
features.includes('v2_suggestions'),
|
||||
instance.api_versions['v2_suggestions.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
@ -1101,7 +1108,7 @@ const getFeatures = (instance?: Instance) => {
|
|||
v.software === FRIENDICA,
|
||||
v.software === ICESHRIMP,
|
||||
v.software === MASTODON,
|
||||
features.includes('v2_suggestions'),
|
||||
instance.api_versions['v2_suggestions.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
/**
|
||||
|
@ -1109,8 +1116,8 @@ const getFeatures = (instance?: Instance) => {
|
|||
* @see POST /api/v1/statuses/:id/translate
|
||||
*/
|
||||
translations: any([
|
||||
features.includes('translation'),
|
||||
features.includes('akkoma:machine_translation'),
|
||||
instance.api_versions['translation.pleroma.pl-api'] >= 1,
|
||||
instance.api_versions['machine_translation.akkoma.pl-api'] >= 1,
|
||||
instance?.configuration.translation.enabled,
|
||||
]),
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pl-api",
|
||||
"version": "0.0.29",
|
||||
"version": "0.0.30",
|
||||
"type": "module",
|
||||
"homepage": "https://github.com/mkljczk/pl-fe/tree/fork/packages/pl-api",
|
||||
"repository": {
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"object-to-formdata": "^4.5.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pl-api": "^0.0.29",
|
||||
"pl-api": "^0.0.30",
|
||||
"postcss": "^8.4.29",
|
||||
"process": "^0.11.10",
|
||||
"punycode": "^2.1.1",
|
||||
|
|
|
@ -86,6 +86,14 @@ const unEmojiReactFail = (statusId: string, emoji: string, error: unknown) => ({
|
|||
error,
|
||||
});
|
||||
|
||||
type EmojiReactsAction =
|
||||
| ReturnType<typeof emojiReactRequest>
|
||||
| ReturnType<typeof emojiReactSuccess>
|
||||
| ReturnType<typeof emojiReactFail>
|
||||
| ReturnType<typeof unEmojiReactRequest>
|
||||
| ReturnType<typeof unEmojiReactSuccess>
|
||||
| ReturnType<typeof unEmojiReactFail>
|
||||
|
||||
export {
|
||||
EMOJI_REACT_REQUEST,
|
||||
EMOJI_REACT_SUCCESS,
|
||||
|
@ -101,4 +109,5 @@ export {
|
|||
unEmojiReactRequest,
|
||||
unEmojiReactSuccess,
|
||||
unEmojiReactFail,
|
||||
type EmojiReactsAction,
|
||||
};
|
||||
|
|
|
@ -418,7 +418,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
|||
return menu;
|
||||
}
|
||||
|
||||
if (status.emoji_reactions.length && features.exposableReactions) {
|
||||
if (status.emoji_reactions.length && features.exposableReactions && features.emojiReactsList) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.viewReactions),
|
||||
action: handleOpenReactionsModal,
|
||||
|
@ -755,7 +755,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
{me && expandable && (features.emojiReacts || features.emojiReactsMastodon) && (
|
||||
{me && expandable && (features.emojiReacts) && (
|
||||
<EmojiPickerDropdown
|
||||
onPickEmoji={handlePickEmoji}
|
||||
theme={statusActionButtonTheme}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { emojiReact, unEmojiReact } from 'pl-fe/actions/emoji-reacts';
|
|||
import { openModal } from 'pl-fe/actions/modals';
|
||||
import EmojiPickerDropdown from 'pl-fe/features/emoji/containers/emoji-picker-dropdown-container';
|
||||
import unicodeMapping from 'pl-fe/features/emoji/mapping';
|
||||
import { useAppDispatch, useLoggedIn, useSettings } from 'pl-fe/hooks';
|
||||
import { useAppDispatch, useFeatures, useLoggedIn, useSettings } from 'pl-fe/hooks';
|
||||
|
||||
import AnimatedNumber from './animated-number';
|
||||
import { Emoji, HStack, Icon, Text } from './ui';
|
||||
|
@ -36,9 +36,10 @@ interface IStatusReaction {
|
|||
const StatusReaction: React.FC<IStatusReaction> = ({ reaction, status, obfuscate, unauthenticated }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const features = useFeatures();
|
||||
|
||||
const bind = useLongPress((e) => {
|
||||
if (e.type !== 'touchstart') return;
|
||||
if (!features.emojiReactsList || e.type !== 'touchstart') return;
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -52,6 +53,7 @@ const StatusReaction: React.FC<IStatusReaction> = ({ reaction, status, obfuscate
|
|||
e.stopPropagation();
|
||||
|
||||
if (unauthenticated) {
|
||||
if (!features.emojiReactsList) return;
|
||||
dispatch(openModal('REACTIONS', { statusId: status.id, reaction: reaction.name }));
|
||||
} else if (reaction.me) {
|
||||
dispatch(unEmojiReact(status, reaction.name));
|
||||
|
|
|
@ -4,7 +4,12 @@ import omit from 'lodash/omit';
|
|||
import { normalizeStatus, normalizeTranslation, Status as StatusRecord } from 'pl-fe/normalizers';
|
||||
import { simulateEmojiReact, simulateUnEmojiReact } from 'pl-fe/utils/emoji-reacts';
|
||||
|
||||
import { EMOJI_REACT_REQUEST, UNEMOJI_REACT_REQUEST } from '../actions/emoji-reacts';
|
||||
import {
|
||||
EMOJI_REACT_FAIL,
|
||||
EMOJI_REACT_REQUEST,
|
||||
UNEMOJI_REACT_REQUEST,
|
||||
type EmojiReactsAction,
|
||||
} from '../actions/emoji-reacts';
|
||||
import {
|
||||
EVENT_JOIN_REQUEST,
|
||||
EVENT_JOIN_FAIL,
|
||||
|
@ -181,7 +186,7 @@ const deleteTranslation = (state: State, statusId: string) => state.deleteIn([st
|
|||
|
||||
const initialState: State = ImmutableMap();
|
||||
|
||||
const statuses = (state = initialState, action: AnyAction): State => {
|
||||
const statuses = (state = initialState, action: EmojiReactsAction | AnyAction): State => {
|
||||
switch (action.type) {
|
||||
case STATUS_IMPORT:
|
||||
return importStatus(state, action.status);
|
||||
|
@ -206,6 +211,7 @@ const statuses = (state = initialState, action: AnyAction): State => {
|
|||
emojiReacts => simulateEmojiReact(emojiReacts as any, action.emoji, action.custom),
|
||||
);
|
||||
case UNEMOJI_REACT_REQUEST:
|
||||
case EMOJI_REACT_FAIL:
|
||||
return state
|
||||
.updateIn(
|
||||
[action.statusId, 'emoji_reactions'],
|
||||
|
|
|
@ -60,7 +60,7 @@ const getAuthUserUrl = (state: RootState) => {
|
|||
|
||||
/** Get the VAPID public key. */
|
||||
const getVapidKey = (state: RootState) =>
|
||||
state.auth.app?.vapid_key || state.instance.pleroma.vapid_public_key;
|
||||
state.auth.app?.vapid_key || state.instance.configuration.vapid.public_key;
|
||||
|
||||
const getMeUrl = (state: RootState) => selectOwnAccount(state)?.url;
|
||||
|
||||
|
|
|
@ -8240,10 +8240,10 @@ pkg-types@^1.0.3:
|
|||
mlly "^1.2.0"
|
||||
pathe "^1.1.0"
|
||||
|
||||
pl-api@^0.0.29:
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.29.tgz#59005a6051932c8b0defb552b1ecda15e1241462"
|
||||
integrity sha512-NDtii+PkJ/pCJHbVx0guAPtfxcXwMYPc7Bm6517I0fJtqhuY3c+QXcaL6DXuQV+OP7+WVcdVIYqIYhhgzCMkXQ==
|
||||
pl-api@^0.0.30:
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-0.0.30.tgz#4b093d9336604e3f1d38d790d9d2908fc36bfa99"
|
||||
integrity sha512-ZTrdcrLPhXQIJSK9smBoN96VngEIrFugmQvwKJPZLJfMXme+kXsV2X4GbNahSByYaoDRFZy6I4vgKicMQlSxig==
|
||||
dependencies:
|
||||
blurhash "^2.0.5"
|
||||
http-link-header "^1.1.3"
|
||||
|
|
Loading…
Reference in a new issue