From ab2e6b2ee385bd96709da154713d3c9f070c6a4f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 23 Apr 2022 15:40:54 -0500 Subject: [PATCH] Features: extensive documentation --- app/soapbox/components/sidebar-navigation.tsx | 6 +- app/soapbox/components/sidebar_menu.tsx | 22 +- app/soapbox/utils/features.ts | 322 +++++++++++++++++- 3 files changed, 322 insertions(+), 28 deletions(-) diff --git a/app/soapbox/components/sidebar-navigation.tsx b/app/soapbox/components/sidebar-navigation.tsx index eede8c871..bb8f47e4d 100644 --- a/app/soapbox/components/sidebar-navigation.tsx +++ b/app/soapbox/components/sidebar-navigation.tsx @@ -81,12 +81,12 @@ const SidebarNavigation = () => { }); } - if (features.localTimeline || features.publicTimeline) { + if (features.publicTimeline) { menu.push(null); } } - if (features.localTimeline) { + if (features.publicTimeline) { menu.push({ to: '/timeline/local', icon: features.federating ? require('@tabler/icons/icons/users.svg') : require('@tabler/icons/icons/world.svg'), @@ -94,7 +94,7 @@ const SidebarNavigation = () => { }); } - if (features.localTimeline && features.federating) { + if (features.publicTimeline && features.federating) { menu.push({ to: '/timeline/fediverse', icon: require('icons/fediverse.svg'), diff --git a/app/soapbox/components/sidebar_menu.tsx b/app/soapbox/components/sidebar_menu.tsx index e7cec5d95..2de745577 100644 --- a/app/soapbox/components/sidebar_menu.tsx +++ b/app/soapbox/components/sidebar_menu.tsx @@ -192,27 +192,25 @@ const SidebarMenu: React.FC = (): JSX.Element | null => { onClick={onClose} /> - {(features.localTimeline || features.publicTimeline) && ( + {features.publicTimeline && <>
- )} - {features.localTimeline && ( } onClick={onClose} /> - )} - {(features.publicTimeline && features.federating) && ( - } - onClick={onClose} - /> - )} + {features.federating && ( + } + onClick={onClose} + /> + )} + }
diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 27982f15d..49e9f7a2e 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -1,4 +1,3 @@ -// Detect backend features to conditionally render elements import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { createSelector } from 'reselect'; import gte from 'semver/functions/gte'; @@ -8,142 +7,432 @@ import { custom } from 'soapbox/custom'; import type { Instance } from 'soapbox/types/entities'; -// Import custom overrides, if exists +/** Import custom overrides, if exists */ const overrides = custom('features'); -// Truthy array convenience function +/** Truthy array convenience function */ const any = (arr: Array): boolean => arr.some(Boolean); -// For uglification -export const MASTODON = 'Mastodon'; -export const PLEROMA = 'Pleroma'; -export const MITRA = 'Mitra'; -export const TRUTHSOCIAL = 'TruthSocial'; -export const PIXELFED = 'Pixelfed'; +/** + * Mastodon, the software upon which this is all based. + * @see {@link https://joinmastodon.org/} + */ +export const MASTODON = 'Mastodon'; +/** + * Pleroma, a feature-rich alternative written in Elixir. + * @see {@link https://pleroma.social/} + */ +export const PLEROMA = 'Pleroma'; + +/** + * Mitra, a Rust backend with deep Ethereum integrations. + * @see {@link https://codeberg.org/silverpill/mitra} + */ +export const MITRA = 'Mitra'; + +/** + * Pixelfed, a federated image sharing platform. + * @see {@link https://pixelfed.org/} + */ +export const PIXELFED = 'Pixelfed'; + +/** + * Truth Social, the Mastodon fork powering truthsocial.com + * @see {@link https://help.truthsocial.com/open-source} + */ +export const TRUTHSOCIAL = 'TruthSocial'; + +/** Parse features for the given instance */ const getInstanceFeatures = (instance: Instance) => { const v = parseVersion(instance.version); const features = instance.pleroma.getIn(['metadata', 'features'], ImmutableList()) as ImmutableList; const federation = instance.pleroma.getIn(['metadata', 'federation'], ImmutableMap()) as ImmutableMap; return { + /** + * Can upload media attachments to statuses. + * @see POST /api/v1/media + * @see POST /api/v1/statuses + */ media: true, + + /** + * Can set privacy scopes on statuses. + * @see POST /api/v1/statuses + */ privacyScopes: v.software !== TRUTHSOCIAL, + + /** + * Can set content warnings on statuses. + * @see POST /api/v1/statuses + */ spoilers: v.software !== TRUTHSOCIAL, + + /** + * Can edit and manage timeline filters (aka "muted words"). + * @see {@link https://docs.joinmastodon.org/methods/accounts/filters/} + */ filters: v.software !== TRUTHSOCIAL, + + /** + * Can add polls to statuses. + * @see POST /api/v1/statuses + */ polls: any([ v.software === MASTODON && gte(v.version, '2.8.0'), v.software === PLEROMA, ]), + + /** + * Can schedule statuses to be posted at a later time. + * @see POST /api/v1/statuses + * @see {@link https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/} + */ scheduledStatuses: any([ v.software === MASTODON && gte(v.version, '2.7.0'), v.software === PLEROMA, ]), + + /** + * Can bookmark statuses. + * @see POST /api/v1/statuses/:id/bookmark + * @see GET /api/v1/bookmarks + */ bookmarks: any([ v.software === MASTODON && gte(v.compatVersion, '3.1.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), v.software === PIXELFED, ]), + + /** + * Can create, view, and manage lists. + * @see {@link https://docs.joinmastodon.org/methods/timelines/lists/} + * @see GET /api/v1/timelines/list/:list_id + */ lists: any([ v.software === MASTODON && gte(v.compatVersion, '2.1.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), ]), + + /** + * Can display suggested accounts. + * @see {@link https://docs.joinmastodon.org/methods/accounts/suggestions/} + */ suggestions: any([ v.software === MASTODON && gte(v.compatVersion, '2.4.3'), v.software === TRUTHSOCIAL, features.includes('v2_suggestions'), ]), + + /** + * Supports V2 suggested accounts. + * @see GET /api/v2/suggestions + */ suggestionsV2: any([ v.software === MASTODON && gte(v.compatVersion, '3.4.0'), v.software === TRUTHSOCIAL, features.includes('v2_suggestions'), ]), + + /** Whether people who blocked you are visible through the API. */ blockersVisible: features.includes('blockers_visible'), + + /** + * Can display trending hashtags. + * @see GET /api/v1/trends + */ trends: any([ v.software === MASTODON && gte(v.compatVersion, '3.0.0'), v.software === TRUTHSOCIAL, ]), + + /** + * Supports V2 media uploads. + * @see POST /api/v2/media + */ mediaV2: any([ v.software === MASTODON && gte(v.compatVersion, '3.1.3'), // Even though Pleroma supports these endpoints, it has disadvantages // v.software === PLEROMA && gte(v.version, '2.1.0'), ]), - localTimeline: any([ - v.software === MASTODON, - v.software === PLEROMA, - ]), + + /** + * Can display a timeline of all known public statuses. + * Local and Fediverse timelines both use this feature. + * @see GET /api/v1/timelines/public + */ publicTimeline: any([ v.software === MASTODON, v.software === PLEROMA, ]), + + /** + * Legacy DMs timeline where messages are displayed chronologically without groupings. + * @see GET /api/v1/timelines/direct + */ directTimeline: any([ v.software === MASTODON && lt(v.compatVersion, '3.0.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), ]), + + /** + * Mastodon's newer solution for direct messaging. + * @see {@link https://docs.joinmastodon.org/methods/timelines/conversations/} + */ conversations: any([ v.software === MASTODON && gte(v.compatVersion, '2.6.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), v.software === PIXELFED, ]), + + /** + * 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 && gte(v.version, '2.0.0'), + + /** + * The backend allows only RGI ("Recommended for General Interchange") emoji reactions. + * @see PUT /api/v1/pleroma/statuses/:id/reactions/:emoji + */ emojiReactsRGI: v.software === PLEROMA && gte(v.version, '2.2.49'), + + /** + * Allows setting the focal point of a media attachment. + * @see {@link https://docs.joinmastodon.org/methods/statuses/media/} + */ focalPoint: v.software === MASTODON && gte(v.compatVersion, '2.3.0'), + + /** + * Pleroma import API. + * @see POST /api/pleroma/follow_import + * @see POST /api/pleroma/blocks_import + * @see POST /api/pleroma/mutes_import + */ importAPI: v.software === PLEROMA, + + /** + * Pleroma import mutes API. + * @see POST /api/pleroma/mutes_import + */ importMutes: v.software === PLEROMA && gte(v.version, '2.2.0'), + + /** + * Soapbox email list. + * @see POST /api/v1/accounts + * @see PATCH /api/v1/accounts/update_credentials + * @see GET /api/v1/pleroma/admin/email_list/subscribers.csv + * @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'), + + /** + * Pleroma chats API. + * @see {@link https://docs.pleroma.social/backend/development/API/chats/} + */ chats: v.software === PLEROMA && gte(v.version, '2.1.0'), + + /** + * Paginated chats API. + * @see GET /api/v2/chats + */ chatsV2: v.software === PLEROMA && gte(v.version, '2.3.0'), + + /** + * List of OAuth scopes supported by both Soapbox and the backend. + * @see POST /api/v1/apps + * @see POST /oauth/token + */ scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push', + + /** Whether the instance federates. */ federating: federation.get('enabled', true) === true, // Assume true unless explicitly false + + /** + * Ability to post statuses in Markdown, BBCode, and HTML. + * @see POST /api/v1/statuses + */ richText: v.software === PLEROMA, + + /** + * Ability to manage account security settings. + * @see POST /api/pleroma/change_password + * @see POST /api/pleroma/change_email + * @see POST /api/pleroma/delete_account + */ securityAPI: any([ v.software === PLEROMA, v.software === TRUTHSOCIAL, ]), + + /** + * Can store client settings in the database. + * @see PATCH /api/v1/accounts/update_credentials + */ settingsStore: any([ v.software === PLEROMA, v.software === TRUTHSOCIAL, ]), + + /** + * Can view and manage ActivityPub aliases through the API. + * @see GET /api/pleroma/aliases + * @see PATCH /api/v1/accounts/update_credentials + */ accountAliasesAPI: v.software === PLEROMA, + + /** + * Can request a password reset email through the API. + * @see POST /auth/password + */ resetPasswordAPI: v.software === PLEROMA, + + /** Whether the accounts who favourited or emoji-reacted to a status can be viewed through the API. */ exposableReactions: features.includes('exposable_reactions'), + + /** + * Ability to subscribe to notifications every time an account posts. + * @see POST /api/v1/pleroma/accounts/:id/subscribe + * @see POST /api/v1/pleroma/accounts/:id/unsubscribe + */ accountSubscriptions: v.software === PLEROMA && gte(v.version, '1.0.0'), + + /** + * Ability to subscribe to notifications every time an account posts. + * @see POST /api/v1/accounts/:id/follow + */ accountNotifies: any([ v.software === MASTODON && gte(v.compatVersion, '3.3.0'), v.software === PLEROMA && gte(v.version, '2.4.50'), ]), + + /** + * Whether the backend allows adding users you don't follow to lists. + * @see POST /api/v1/lists/:id/accounts + */ unrestrictedLists: v.software === PLEROMA, + + /** + * The accounts API allows an acct instead of an ID. + * @see GET /api/v1/accounts/:acct_or_id + */ accountByUsername: v.software === PLEROMA, + + /** + * A directory of discoverable profiles from the instance. + * @see {@link https://docs.joinmastodon.org/methods/instance/directory/} + */ profileDirectory: any([ v.software === MASTODON && gte(v.compatVersion, '3.0.0'), features.includes('profile_directory'), ]), + + /** + * Look up an account by the acct. + * @see GET /api/v1/accounts/lookup + */ accountLookup: any([ v.software === MASTODON && gte(v.compatVersion, '3.4.0'), v.software === PLEROMA && gte(v.version, '2.4.50'), ]), + + /** + * Interact with statuses from another instance while logged-out. + * @see POST /api/v1/pleroma/remote_interaction + */ remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'), + + /** + * Ability to address recipients of a status explicitly (with `to`). + * @see POST /api/v1/statuses + */ explicitAddressing: any([ v.software === PLEROMA && gte(v.version, '1.0.0'), v.software === TRUTHSOCIAL, ]), + + /** + * Ability to pin other accounts on one's profile. + * @see POST /api/v1/accounts/:id/pin + * @see POST /api/v1/accounts/:id/unpin + * @see GET /api/v1/pleroma/accounts/:id/endorsements + */ accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'), + + /** + * Ability to quote posts in statuses. + * @see POST /api/v1/statuses + */ quotePosts: any([ v.software === PLEROMA && gte(v.version, '2.4.50'), instance.feature_quote === true, ]), + + /** + * Set your birthday and view upcoming birthdays. + * @see GET /api/v1/pleroma/birthdays + * @see POST /api/v1/accounts + * @see PATCH /api/v1/accounts/update_credentials + */ birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'), + + /** + * Sign in with an Ethereum wallet. + * @see POST /oauth/token + */ ethereumLogin: v.software === MITRA, + + /** + * Move followers to a different ActivityPub account. + * @see POST /api/pleroma/move_account + */ accountMoving: v.software === PLEROMA && gte(v.version, '2.4.50'), + + /** + * Add private notes to accounts. + * @see POST /api/v1/accounts/:id/note + * @see GET /api/v1/accounts/relationships + */ notes: any([ v.software === MASTODON && gte(v.compatVersion, '3.2.0'), v.software === PLEROMA && gte(v.version, '2.4.50'), ]), + + /** + * Truth Social trending statuses API. + * @see GET /api/v1/truth/trending/truths + */ trendingTruths: v.software === TRUTHSOCIAL, + + /** + * Trending statuses. + * @see GET /api/v1/trends/statuses + */ trendingStatuses: v.software === MASTODON && gte(v.compatVersion, '3.5.0'), + + /** Truth Social account registration API. */ pepe: v.software === TRUTHSOCIAL, + + /** + * Ability to set one's location on their profile. + * @see PATCH /api/v1/accounts/update_credentials + */ accountLocation: v.software === TRUTHSOCIAL, + + /** + * Ability to set one's website on their profile. + * @see PATCH /api/v1/accounts/update_credentials + */ accountWebsite: v.software === TRUTHSOCIAL, + + /** + * Whether client settings can be retrieved from the API. + * @see GET /api/pleroma/frontend_configurations + */ frontendConfigurations: v.software === PLEROMA, // FIXME: long-term this shouldn't be a feature, @@ -152,8 +441,10 @@ const getInstanceFeatures = (instance: Instance) => { }; }; +/** Features available from a backend */ export type Features = ReturnType; +/** Detect backend features to conditionally render elements */ export const getFeatures = createSelector([ (instance: Instance) => instance, ], (instance): Features => { @@ -161,12 +452,17 @@ export const getFeatures = createSelector([ return Object.assign(features, overrides) as Features; }); +/** Fediverse backend */ interface Backend { + /** Name of the software */ software: string | null, + /** API version number */ version: string, + /** Mastodon API version this backend is compatible with */ compatVersion: string, } +/** Get information about the software from its version string */ export const parseVersion = (version: string): Backend => { const regex = /^([\w.]*)(?: \(compatible; ([\w]*) (.*)\))?$/; const match = regex.exec(version);