Mangane/app/soapbox/utils/features.ts

175 lines
6.3 KiB
TypeScript
Raw Normal View History

2020-05-17 21:44:33 +02:00
// Detect backend features to conditionally render elements
2021-08-23 21:14:47 +02:00
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { createSelector } from 'reselect';
import gte from 'semver/functions/gte';
import lt from 'semver/functions/lt';
2020-05-17 21:44:33 +02:00
2022-03-04 06:38:59 +01:00
import { custom } from 'soapbox/custom';
import type { Instance } from 'soapbox/types/entities';
// Import custom overrides, if exists
2022-03-04 06:38:59 +01:00
const overrides = custom('features');
2022-03-04 06:38:59 +01:00
// Truthy array convenience function
const any = (arr: Array<any>): boolean => arr.some(Boolean);
// For uglification
export const MASTODON = 'Mastodon';
export const PLEROMA = 'Pleroma';
export const MITRA = 'Mitra';
export const TRUTHSOCIAL = 'TruthSocial';
2022-04-11 01:21:52 +02:00
export const PIXELFED = 'Pixelfed';
const getInstanceFeatures = (instance: Instance) => {
2022-03-30 23:30:04 +02:00
const v = parseVersion(instance.version);
const features = instance.pleroma.getIn(['metadata', 'features'], ImmutableList()) as ImmutableList<string>;
const federation = instance.pleroma.getIn(['metadata', 'federation'], ImmutableMap()) as ImmutableMap<string, any>;
2022-02-13 03:08:38 +01:00
return {
2022-03-04 19:47:59 +01:00
media: true,
privacyScopes: v.software !== TRUTHSOCIAL,
spoilers: v.software !== TRUTHSOCIAL,
filters: v.software !== TRUTHSOCIAL,
2022-03-04 19:47:59 +01:00
polls: any([
v.software === MASTODON && gte(v.version, '2.8.0'),
v.software === PLEROMA,
]),
scheduledStatuses: any([
v.software === MASTODON && gte(v.version, '2.7.0'),
v.software === PLEROMA,
]),
bookmarks: any([
v.software === MASTODON && gte(v.compatVersion, '3.1.0'),
v.software === PLEROMA && gte(v.version, '0.9.9'),
2022-04-11 01:21:52 +02:00
v.software === PIXELFED,
]),
2021-09-18 21:36:42 +02:00
lists: any([
v.software === MASTODON && gte(v.compatVersion, '2.1.0'),
v.software === PLEROMA && gte(v.version, '0.9.9'),
]),
suggestions: any([
v.software === MASTODON && gte(v.compatVersion, '2.4.3'),
v.software === TRUTHSOCIAL,
features.includes('v2_suggestions'),
]),
suggestionsV2: any([
v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
v.software === TRUTHSOCIAL,
features.includes('v2_suggestions'),
]),
blockersVisible: features.includes('blockers_visible'),
trends: any([
v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
v.software === TRUTHSOCIAL,
]),
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'),
]),
directTimeline: any([
v.software === MASTODON && lt(v.compatVersion, '3.0.0'),
v.software === PLEROMA && gte(v.version, '0.9.9'),
]),
conversations: any([
v.software === MASTODON && gte(v.compatVersion, '2.6.0'),
v.software === PLEROMA && gte(v.version, '0.9.9'),
2022-04-11 01:21:52 +02:00
v.software === PIXELFED,
]),
emojiReacts: v.software === PLEROMA && gte(v.version, '2.0.0'),
emojiReactsRGI: v.software === PLEROMA && gte(v.version, '2.2.49'),
focalPoint: v.software === MASTODON && gte(v.compatVersion, '2.3.0'),
importAPI: v.software === PLEROMA,
importMutes: v.software === PLEROMA && gte(v.version, '2.2.0'),
2021-08-23 21:14:47 +02:00
emailList: features.includes('email_list'),
chats: v.software === PLEROMA && gte(v.version, '2.1.0'),
chatsV2: v.software === PLEROMA && gte(v.version, '2.3.0'),
scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push',
federating: federation.get('enabled', true) === true, // Assume true unless explicitly false
richText: v.software === PLEROMA,
securityAPI: any([
v.software === PLEROMA,
v.software === TRUTHSOCIAL,
]),
settingsStore: any([
v.software === PLEROMA,
v.software === TRUTHSOCIAL,
]),
accountAliasesAPI: v.software === PLEROMA,
resetPasswordAPI: v.software === PLEROMA,
exposableReactions: features.includes('exposable_reactions'),
accountSubscriptions: v.software === PLEROMA && gte(v.version, '1.0.0'),
accountNotifies: any([
v.software === MASTODON && gte(v.compatVersion, '3.3.0'),
v.software === PLEROMA && gte(v.version, '2.4.50'),
]),
unrestrictedLists: v.software === PLEROMA,
2021-09-18 22:50:29 +02:00
accountByUsername: v.software === PLEROMA,
profileDirectory: any([
v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
features.includes('profile_directory'),
]),
accountLookup: any([
v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
v.software === PLEROMA && gte(v.version, '2.4.50'),
]),
2022-01-02 21:43:53 +01:00
remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'),
explicitAddressing: any([
v.software === PLEROMA && gte(v.version, '1.0.0'),
v.software === TRUTHSOCIAL,
]),
accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'),
2022-02-13 03:08:38 +01:00
quotePosts: any([
v.software === PLEROMA && gte(v.version, '2.4.50'),
instance.feature_quote === true,
2022-02-13 03:08:38 +01:00
]),
birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'),
2022-02-10 02:47:13 +01:00
ethereumLogin: v.software === MITRA,
accountMoving: v.software === PLEROMA && gte(v.version, '2.4.50'),
notes: any([
v.software === MASTODON && gte(v.compatVersion, '3.2.0'),
v.software === PLEROMA && gte(v.version, '2.4.50'),
]),
trendingTruths: v.software === TRUTHSOCIAL,
trendingStatuses: v.software === MASTODON && gte(v.compatVersion, '3.5.0'),
pepe: v.software === TRUTHSOCIAL,
};
};
2022-03-30 22:50:16 +02:00
export type Features = ReturnType<typeof getInstanceFeatures>;
export const getFeatures = createSelector([
(instance: Instance) => instance,
], (instance): Features => {
const features = getInstanceFeatures(instance);
return Object.assign(features, overrides) as Features;
});
2020-05-17 21:44:33 +02:00
interface Backend {
software: string | null,
version: string,
compatVersion: string,
}
export const parseVersion = (version: string): Backend => {
const regex = /^([\w.]*)(?: \(compatible; ([\w]*) (.*)\))?$/;
const match = regex.exec(version);
2022-02-07 03:46:06 +01:00
if (match) {
return {
software: match[2] || MASTODON,
version: match[3] || match[1],
compatVersion: match[1],
};
} else {
// If we can't parse the version, this is a new and exotic backend.
// Fall back to minimal featureset.
return {
software: null,
version: '0.0.0',
compatVersion: '0.0.0',
};
}
2020-05-17 21:44:33 +02:00
};