From 8794c293aa2750d0d950f83c0d68368fe560853b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 13 Sep 2024 13:05:06 +0200 Subject: [PATCH] Support reactions on Mastodon forks, support api_versions, minor fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-api/lib/client.ts | 46 ++++- packages/pl-api/lib/entities/account.ts | 18 +- .../pl-api/lib/entities/emoji-reaction.ts | 9 +- packages/pl-api/lib/entities/instance.ts | 176 +++++++++--------- packages/pl-api/lib/entities/notification.ts | 2 + packages/pl-api/lib/features.ts | 71 +++---- packages/pl-api/package.json | 2 +- packages/pl-fe/package.json | 2 +- packages/pl-fe/src/actions/emoji-reacts.ts | 9 + .../src/components/status-action-bar.tsx | 4 +- .../src/components/status-reactions-bar.tsx | 6 +- packages/pl-fe/src/reducers/statuses.ts | 10 +- packages/pl-fe/src/utils/auth.ts | 2 +- packages/pl-fe/yarn.lock | 8 +- 14 files changed, 220 insertions(+), 145 deletions(-) diff --git a/packages/pl-api/lib/client.ts b/packages/pl-api/lib/client.ts index 6928fa3ec..2d67268d5 100644 --- a/packages/pl-api/lib/client.ts +++ b/packages/pl-api/lib/client.ts @@ -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, 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); }, diff --git a/packages/pl-api/lib/entities/account.ts b/packages/pl-api/lib/entities/account.ts index 5bb56d081..9bdfa0a67 100644 --- a/packages/pl-api/lib/entities/account.ts +++ b/packages/pl-api/lib/entities/account.ts @@ -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, }; }; diff --git a/packages/pl-api/lib/entities/emoji-reaction.ts b/packages/pl-api/lib/entities/emoji-reaction.ts index 2401f47ae..45e335bf8 100644 --- a/packages/pl-api/lib/entities/emoji-reaction.ts +++ b/packages/pl-api/lib/entities/emoji-reaction.ts @@ -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; diff --git a/packages/pl-api/lib/entities/instance.ts b/packages/pl-api/lib/entities/instance.ts index bd7b0bf5b..808573294 100644 --- a/packages/pl-api/lib/entities/instance.ts +++ b/packages/pl-api/lib/entities/instance.ts @@ -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, }; })); diff --git a/packages/pl-api/lib/entities/notification.ts b/packages/pl-api/lib/entities/notification.ts index 818fb85a3..41d3cda7d 100644 --- a/packages/pl-api/lib/entities/notification.ts +++ b/packages/pl-api/lib/entities/notification.ts @@ -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 = z.preprocess((notification: any) => ({ + group_key: `ungrouped-${notification.id}`, ...pick(notification.pleroma || {}, ['is_muted', 'is_seen']), ...notification, type: notification.type === 'pleroma:report' diff --git a/packages/pl-api/lib/features.ts b/packages/pl-api/lib/features.ts index 24fd7b49a..83fb6a941 100644 --- a/packages/pl-api/lib/features.ts +++ b/packages/pl-api/lib/features.ts @@ -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, ]), diff --git a/packages/pl-api/package.json b/packages/pl-api/package.json index 11e1eb58f..7cbb87665 100644 --- a/packages/pl-api/package.json +++ b/packages/pl-api/package.json @@ -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": { diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index 55fd1e6ca..1293a0b66 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -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", diff --git a/packages/pl-fe/src/actions/emoji-reacts.ts b/packages/pl-fe/src/actions/emoji-reacts.ts index dcc34cbe0..5643b8b8f 100644 --- a/packages/pl-fe/src/actions/emoji-reacts.ts +++ b/packages/pl-fe/src/actions/emoji-reacts.ts @@ -86,6 +86,14 @@ const unEmojiReactFail = (statusId: string, emoji: string, error: unknown) => ({ error, }); +type EmojiReactsAction = + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + export { EMOJI_REACT_REQUEST, EMOJI_REACT_SUCCESS, @@ -101,4 +109,5 @@ export { unEmojiReactRequest, unEmojiReactSuccess, unEmojiReactFail, + type EmojiReactsAction, }; diff --git a/packages/pl-fe/src/components/status-action-bar.tsx b/packages/pl-fe/src/components/status-action-bar.tsx index 8691ed0bb..cf6b538b8 100644 --- a/packages/pl-fe/src/components/status-action-bar.tsx +++ b/packages/pl-fe/src/components/status-action-bar.tsx @@ -418,7 +418,7 @@ const StatusActionBar: React.FC = ({ 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 = ({ /> )} - {me && expandable && (features.emojiReacts || features.emojiReactsMastodon) && ( + {me && expandable && (features.emojiReacts) && ( = ({ 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 = ({ 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)); diff --git a/packages/pl-fe/src/reducers/statuses.ts b/packages/pl-fe/src/reducers/statuses.ts index 480e5822d..e0c66e39b 100644 --- a/packages/pl-fe/src/reducers/statuses.ts +++ b/packages/pl-fe/src/reducers/statuses.ts @@ -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'], diff --git a/packages/pl-fe/src/utils/auth.ts b/packages/pl-fe/src/utils/auth.ts index b82d933c7..671a35cf7 100644 --- a/packages/pl-fe/src/utils/auth.ts +++ b/packages/pl-fe/src/utils/auth.ts @@ -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; diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index 6109d4909..df2acf321 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -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"