2020-05-17 12:44:33 -07:00
|
|
|
// Detect backend features to conditionally render elements
|
2021-08-23 12:14:47 -07:00
|
|
|
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
2021-07-06 11:06:21 -07:00
|
|
|
import { createSelector } from 'reselect';
|
2022-01-10 14:17:52 -08:00
|
|
|
import gte from 'semver/functions/gte';
|
|
|
|
import lt from 'semver/functions/lt';
|
2020-05-17 12:44:33 -07:00
|
|
|
|
2022-03-03 21:38:59 -08:00
|
|
|
import { custom } from 'soapbox/custom';
|
2022-03-03 21:05:37 -08:00
|
|
|
|
2022-03-26 12:23:29 -07:00
|
|
|
import type { Instance } from 'soapbox/types/entities';
|
|
|
|
|
2022-03-03 21:05:37 -08:00
|
|
|
// Import custom overrides, if exists
|
2022-03-03 21:38:59 -08:00
|
|
|
const overrides = custom('features');
|
2022-03-03 21:05:37 -08:00
|
|
|
|
2022-03-03 21:38:59 -08:00
|
|
|
// Truthy array convenience function
|
2022-03-26 12:23:29 -07:00
|
|
|
const any = (arr: Array<any>): boolean => arr.some(Boolean);
|
2021-09-18 12:27:15 -07:00
|
|
|
|
|
|
|
// For uglification
|
2022-03-26 12:06:25 -07:00
|
|
|
export const MASTODON = 'Mastodon';
|
|
|
|
export const PLEROMA = 'Pleroma';
|
|
|
|
export const MITRA = 'Mitra';
|
|
|
|
export const TRUTHSOCIAL = 'TruthSocial';
|
2022-04-10 16:21:52 -07:00
|
|
|
export const PIXELFED = 'Pixelfed';
|
2021-09-18 12:27:15 -07:00
|
|
|
|
2022-03-26 12:23:29 -07:00
|
|
|
const getInstanceFeatures = (instance: Instance) => {
|
2022-03-30 14:30:04 -07:00
|
|
|
const v = parseVersion(instance.version);
|
2022-03-26 12:23:29 -07:00
|
|
|
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-12 18:08:38 -08:00
|
|
|
|
2022-03-26 12:23:29 -07:00
|
|
|
return {
|
2022-03-04 10:47:59 -08:00
|
|
|
media: true,
|
2022-03-26 12:06:25 -07:00
|
|
|
privacyScopes: v.software !== TRUTHSOCIAL,
|
|
|
|
spoilers: v.software !== TRUTHSOCIAL,
|
|
|
|
filters: v.software !== TRUTHSOCIAL,
|
2022-03-04 10:47:59 -08: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,
|
|
|
|
]),
|
2021-09-18 12:27:15 -07:00
|
|
|
bookmarks: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.1.0'),
|
|
|
|
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
2022-04-10 16:21:52 -07:00
|
|
|
v.software === PIXELFED,
|
2021-09-18 12:27:15 -07:00
|
|
|
]),
|
2021-09-18 12:36:42 -07:00
|
|
|
lists: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '2.1.0'),
|
|
|
|
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
|
|
|
]),
|
2021-11-26 21:36:17 -08:00
|
|
|
suggestions: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '2.4.3'),
|
2022-03-30 08:58:06 -07:00
|
|
|
v.software === TRUTHSOCIAL,
|
2021-11-26 21:36:17 -08:00
|
|
|
features.includes('v2_suggestions'),
|
|
|
|
]),
|
|
|
|
suggestionsV2: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
|
2022-03-30 08:58:06 -07:00
|
|
|
v.software === TRUTHSOCIAL,
|
2021-11-26 21:36:17 -08:00
|
|
|
features.includes('v2_suggestions'),
|
|
|
|
]),
|
2022-01-14 17:06:31 -08:00
|
|
|
blockersVisible: features.includes('blockers_visible'),
|
2022-03-30 08:58:06 -07:00
|
|
|
trends: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
|
|
|
|
v.software === TRUTHSOCIAL,
|
|
|
|
]),
|
2021-10-05 10:59:37 -07:00
|
|
|
mediaV2: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.1.3'),
|
2021-10-19 09:28:48 -07:00
|
|
|
// Even though Pleroma supports these endpoints, it has disadvantages
|
|
|
|
// v.software === PLEROMA && gte(v.version, '2.1.0'),
|
2021-10-05 10:59:37 -07:00
|
|
|
]),
|
2022-04-12 18:10:47 -07:00
|
|
|
localTimeline: any([
|
|
|
|
v.software === MASTODON,
|
|
|
|
v.software === PLEROMA,
|
|
|
|
]),
|
|
|
|
publicTimeline: any([
|
|
|
|
v.software === MASTODON,
|
|
|
|
v.software === PLEROMA,
|
|
|
|
]),
|
2021-10-05 15:24:01 -07:00
|
|
|
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-10 16:21:52 -07:00
|
|
|
v.software === PIXELFED,
|
2021-10-05 15:24:01 -07:00
|
|
|
]),
|
2021-09-18 12:27:15 -07:00
|
|
|
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'),
|
2021-10-31 14:07:41 -07:00
|
|
|
importAPI: v.software === PLEROMA,
|
2021-09-18 12:27:15 -07:00
|
|
|
importMutes: v.software === PLEROMA && gte(v.version, '2.2.0'),
|
2021-08-23 12:14:47 -07:00
|
|
|
emailList: features.includes('email_list'),
|
2021-09-18 12:27:15 -07:00
|
|
|
chats: v.software === PLEROMA && gte(v.version, '2.1.0'),
|
2021-12-14 04:38:59 -08:00
|
|
|
chatsV2: v.software === PLEROMA && gte(v.version, '2.3.0'),
|
2021-09-18 12:27:15 -07:00
|
|
|
scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push',
|
2022-04-10 13:44:51 -07:00
|
|
|
federating: federation.get('enabled', true) === true, // Assume true unless explicitly false
|
2021-09-18 12:27:15 -07:00
|
|
|
richText: v.software === PLEROMA,
|
2022-03-26 12:06:25 -07:00
|
|
|
securityAPI: any([
|
|
|
|
v.software === PLEROMA,
|
|
|
|
v.software === TRUTHSOCIAL,
|
|
|
|
]),
|
|
|
|
settingsStore: any([
|
|
|
|
v.software === PLEROMA,
|
|
|
|
v.software === TRUTHSOCIAL,
|
|
|
|
]),
|
2021-09-18 12:27:15 -07:00
|
|
|
accountAliasesAPI: v.software === PLEROMA,
|
|
|
|
resetPasswordAPI: v.software === PLEROMA,
|
2021-09-09 11:44:12 -07:00
|
|
|
exposableReactions: features.includes('exposable_reactions'),
|
2021-09-18 12:27:15 -07:00
|
|
|
accountSubscriptions: v.software === PLEROMA && gte(v.version, '1.0.0'),
|
2021-12-30 07:13:45 -08:00
|
|
|
accountNotifies: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.3.0'),
|
|
|
|
v.software === PLEROMA && gte(v.version, '2.4.50'),
|
|
|
|
]),
|
2021-09-18 12:27:15 -07:00
|
|
|
unrestrictedLists: v.software === PLEROMA,
|
2021-09-18 13:50:29 -07:00
|
|
|
accountByUsername: v.software === PLEROMA,
|
2021-12-25 09:21:20 -08:00
|
|
|
profileDirectory: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
|
|
|
|
features.includes('profile_directory'),
|
|
|
|
]),
|
2021-12-29 14:54:16 -08:00
|
|
|
accountLookup: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
|
|
|
|
v.software === PLEROMA && gte(v.version, '2.4.50'),
|
|
|
|
]),
|
2022-01-02 12:43:53 -08:00
|
|
|
remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
2022-03-26 12:06:25 -07:00
|
|
|
explicitAddressing: any([
|
|
|
|
v.software === PLEROMA && gte(v.version, '1.0.0'),
|
|
|
|
v.software === TRUTHSOCIAL,
|
|
|
|
]),
|
2022-01-10 12:19:38 -08:00
|
|
|
accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
2022-02-12 18:08:38 -08:00
|
|
|
quotePosts: any([
|
|
|
|
v.software === PLEROMA && gte(v.version, '2.4.50'),
|
2022-03-26 12:23:29 -07:00
|
|
|
instance.feature_quote === true,
|
2022-02-12 18:08:38 -08:00
|
|
|
]),
|
2022-01-25 12:09:30 -08:00
|
|
|
birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
2022-02-09 17:47:13 -08:00
|
|
|
ethereumLogin: v.software === MITRA,
|
2022-02-14 12:00:41 -08:00
|
|
|
accountMoving: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
2022-02-26 06:57:09 -08:00
|
|
|
notes: any([
|
|
|
|
v.software === MASTODON && gte(v.compatVersion, '3.2.0'),
|
|
|
|
v.software === PLEROMA && gte(v.version, '2.4.50'),
|
|
|
|
]),
|
2022-03-29 12:15:06 -07:00
|
|
|
trendingTruths: v.software === TRUTHSOCIAL,
|
|
|
|
trendingStatuses: v.software === MASTODON && gte(v.compatVersion, '3.5.0'),
|
2022-04-05 12:38:49 -07:00
|
|
|
pepe: v.software === TRUTHSOCIAL,
|
2022-04-12 17:08:31 -07:00
|
|
|
|
|
|
|
// FIXME: long-term this shouldn't be a feature,
|
|
|
|
// but for now we want it to be overrideable in the build
|
|
|
|
darkMode: true,
|
2022-03-26 12:23:29 -07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-03-30 13:50:16 -07:00
|
|
|
export type Features = ReturnType<typeof getInstanceFeatures>;
|
2022-03-26 12:23:29 -07:00
|
|
|
|
|
|
|
export const getFeatures = createSelector([
|
|
|
|
(instance: Instance) => instance,
|
|
|
|
], (instance): Features => {
|
|
|
|
const features = getInstanceFeatures(instance);
|
|
|
|
return Object.assign(features, overrides) as Features;
|
2021-07-06 11:06:21 -07:00
|
|
|
});
|
2020-05-17 12:44:33 -07:00
|
|
|
|
2022-03-26 12:23:29 -07:00
|
|
|
interface Backend {
|
|
|
|
software: string | null,
|
|
|
|
version: string,
|
|
|
|
compatVersion: string,
|
|
|
|
}
|
|
|
|
|
|
|
|
export const parseVersion = (version: string): Backend => {
|
|
|
|
const regex = /^([\w.]*)(?: \(compatible; ([\w]*) (.*)\))?$/;
|
2021-08-03 10:10:42 -07:00
|
|
|
const match = regex.exec(version);
|
2022-02-06 18:46:06 -08: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 12:44:33 -07:00
|
|
|
};
|