diff --git a/app/soapbox/normalizers/instance.ts b/app/soapbox/normalizers/instance.ts index 35d419392..fa7700fb5 100644 --- a/app/soapbox/normalizers/instance.ts +++ b/app/soapbox/normalizers/instance.ts @@ -140,6 +140,9 @@ export const normalizeInstance = (instance: Record) => { return isNumber(value) ? value : getAttachmentLimit(software); }); + // Urls can't be null, fix for Friendica + if (instance.get('urls') === null) instance.delete('urls'); + // Normalize version normalizeVersion(instance); fixTakahe(instance); diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 9bc89ff85..08f6a50b0 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -1,6 +1,7 @@ /* eslint sort-keys: "error" */ import { List as ImmutableList, Map as ImmutableMap } from 'immutable'; import { createSelector } from 'reselect'; +import semverCoerce from 'semver/functions/coerce'; import gte from 'semver/functions/gte'; import lt from 'semver/functions/lt'; import semverParse from 'semver/functions/parse'; @@ -15,18 +16,18 @@ const overrides = custom('features'); /** Truthy array convenience function */ const any = (arr: Array): boolean => arr.some(Boolean); +/** + * Friendica. + * @see {@link https://friendi.ca/} + */ +export const FRIENDICA = 'Friendica'; + /** * 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} @@ -40,29 +41,10 @@ export const MITRA = 'Mitra'; export const PIXELFED = 'Pixelfed'; /** - * Truth Social, the Mastodon fork powering truthsocial.com - * @see {@link https://help.truthsocial.com/open-source} + * Pleroma, a feature-rich alternative written in Elixir. + * @see {@link https://pleroma.social/} */ -export const TRUTHSOCIAL = 'TruthSocial'; - -/** - * Rebased, the recommended backend for Soapbox. - * @see {@link https://gitlab.com/soapbox-pub/rebased} - */ -// NOTE: Rebased is named 'soapbox' for legacy reasons. -export const REBASED = 'soapbox'; - -/** - * glitch-soc, fork of Mastodon with a number of experimental features. - * @see {@link https://glitch-soc.github.io/docs/} - */ -export const GLITCH = 'glitch'; - -/** - * Akkoma, a Pleroma fork. - * @see {@link https://akkoma.dev/AkkomaGang/akkoma} - */ -export const AKKOMA = 'akkoma'; +export const PLEROMA = 'Pleroma'; /** * Takahē, backend with support for serving multiple domains. @@ -70,11 +52,36 @@ export const AKKOMA = 'akkoma'; */ export const TAKAHE = 'Takahe'; +/** + * Truth Social, the Mastodon fork powering truthsocial.com + * @see {@link https://help.truthsocial.com/open-source} + */ +export const TRUTHSOCIAL = 'TruthSocial'; + /** * Wildebeest, backend running on top of Cloudflare Pages. */ export const WILDEBEEST = 'Wildebeest'; +/** + * Akkoma, a Pleroma fork. + * @see {@link https://akkoma.dev/AkkomaGang/akkoma} + */ +export const AKKOMA = 'akkoma'; + +/** + * glitch-soc, fork of Mastodon with a number of experimental features. + * @see {@link https://glitch-soc.github.io/docs/} + */ +export const GLITCH = 'glitch'; + +/** + * Rebased, the recommended backend for Soapbox. + * @see {@link https://gitlab.com/soapbox-pub/rebased} + */ +// NOTE: Rebased is named 'soapbox' for legacy reasons. +export const REBASED = 'soapbox'; + /** Parse features for the given instance */ const getInstanceFeatures = (instance: Instance) => { const v = parseVersion(instance.version); @@ -207,6 +214,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see GET /api/v1/bookmarks */ bookmarks: any([ + v.software === FRIENDICA, v.software === MASTODON && gte(v.compatVersion, '3.1.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), v.software === PIXELFED, @@ -301,6 +309,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see {@link https://docs.joinmastodon.org/methods/timelines/conversations/} */ conversations: any([ + v.software === FRIENDICA, v.software === MASTODON && gte(v.compatVersion, '2.6.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), v.software === PIXELFED, @@ -312,6 +321,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see GET /api/v1/timelines/direct */ directTimeline: any([ + v.software === FRIENDICA, v.software === MASTODON && lt(v.compatVersion, '3.0.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), ]), @@ -321,6 +331,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see PATCH /api/v1/accounts/update_credentials */ editProfile: any([ + v.software === FRIENDICA, v.software === MASTODON, v.software === MITRA, v.software === PIXELFED, @@ -479,6 +490,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see GET /api/v1/timelines/list/:list_id */ lists: any([ + v.software === FRIENDICA, v.software === MASTODON && gte(v.compatVersion, '2.1.0'), v.software === PLEROMA && gte(v.version, '0.9.9'), ]), @@ -592,6 +604,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see {@link https://docs.joinmastodon.org/methods/instance/directory/} */ profileDirectory: any([ + v.software === FRIENDICA, v.software === MASTODON && gte(v.compatVersion, '3.0.0'), features.includes('profile_directory'), ]), @@ -726,6 +739,7 @@ const getInstanceFeatures = (instance: Instance) => { * @see {@link https://docs.joinmastodon.org/methods/accounts/suggestions/} */ suggestions: any([ + v.software === FRIENDICA, v.software === MASTODON && gte(v.compatVersion, '2.4.3'), v.software === TRUTHSOCIAL, features.includes('v2_suggestions'), @@ -816,9 +830,10 @@ export const parseVersion = (version: string): Backend => { const regex = /^([\w+.]*)(?: \(compatible; ([\w]*) (.*)\))?$/; const match = regex.exec(version); - const semver = match ? semverParse(match[3] || match[1]) : null; + const semver = match ? semverParse(semverCoerce(match[3] || match[1], { loose: true })) : null; const compat = match ? semverParse(match[1]) : null; + if (match && semver && compat) { return { build: semver.build[0], diff --git a/package.json b/package.json index 9230aa400..06f47abdc 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "sass": "^1.20.3", "sass-loader": "^13.0.0", "seedrandom": "^3.0.5", - "semver": "^7.3.2", + "semver": "^7.3.8", "stringz": "^2.0.0", "substring-trie": "^1.0.2", "terser-webpack-plugin": "^5.2.3", diff --git a/yarn.lock b/yarn.lock index 2aa5a0c0f..a1fb69a9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10220,7 +10220,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.5, semver@7.x, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.3.5, semver@7.x, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==