diff --git a/src/components/list-add-edit.jsx b/src/components/list-add-edit.jsx index d9a21a4b..8630d712 100644 --- a/src/components/list-add-edit.jsx +++ b/src/components/list-add-edit.jsx @@ -24,7 +24,7 @@ function ListAddEdit({ list, onClose }) { } } }, [editMode]); - const supportsExclusive = supports('@mastodon/list-exclusive'); + const supportsExclusive = supports('@mastodon/list-exclusive') || supports('@gotosocial/list-exclusive'); return (
diff --git a/src/data/features.json b/src/data/features.json new file mode 100644 index 00000000..7376418b --- /dev/null +++ b/src/data/features.json @@ -0,0 +1,9 @@ +{ + "@mastodon/edit-media-attributes": ">=4.1", + "@mastodon/list-exclusive": ">=4.2", + "@gotosocial/list-exclusive": ">=0.17", + "@mastodon/filtered-notifications": "~4.3 || >=4.3", + "@mastodon/fetch-multiple-statuses": "~4.3 || >=4.3", + "@mastodon/trending-link-posts": "~4.3 || >=4.3", + "@mastodon/grouped-notifications": "~4.3 || >=4.3" +} diff --git a/src/utils/api.js b/src/utils/api.js index c2f742ad..4ea12ddc 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -89,29 +89,26 @@ export async function initInstance(client, instance) { configuration: { urls: { streaming } = {} } = {}, } = info; - let nodeInfo; + // GoToSocial requires we get the NodeInfo to identify server type + // spec: https://github.com/jhass/nodeinfo try { if (uri || domain) { let urlBase = uri || `https://${domain}`; - const wellKnownResponse = await fetch(`${urlBase}/.well-known/nodeinfo`); - if (wellKnownResponse.ok) { - const wellKnown = await wellKnownResponse.json(); - if (wellKnown && Array.isArray(wellKnown.links)) { - const nodeInfoUrl = wellKnown.links.find( - (link) => typeof link.rel === 'string' && - link.rel.startsWith('http://nodeinfo.diaspora.software/ns/schema/') - )?.href; - if (nodeInfoUrl && nodeInfoUrl.startsWith(urlBase)) { - const nodeInfoResponse = await fetch(nodeInfoUrl); - nodeInfo = await nodeInfoResponse.json(); + const wellKnown = await (await fetch(`${urlBase}/.well-known/nodeinfo`)).json(); + if (Array.isArray(wellKnown?.links)) { + const nodeInfoUrl = wellKnown.links.find( + (link) => typeof link.rel === 'string' && + link.rel.startsWith('http://nodeinfo.diaspora.software/ns/schema/') + )?.href; + if (nodeInfoUrl && nodeInfoUrl.startsWith(urlBase)) { + const nodeInfo = await (await fetch(nodeInfoUrl)).json(); + if (typeof nodeInfo?.software?.name === 'string') { + info.software_name = nodeInfo.software.name; } } } } } catch (e) {} - if (nodeInfo) { - info.nodeInfo = nodeInfo; - } console.log(info); const instances = store.local.getJSON('instances') || {}; diff --git a/src/utils/store-utils.js b/src/utils/store-utils.js index c2caf9e5..cc236215 100644 --- a/src/utils/store-utils.js +++ b/src/utils/store-utils.js @@ -163,5 +163,5 @@ export function getVapidKey() { export function isMediaFirstInstance() { const instance = getCurrentInstance(); - return instance.nodeInfo?.software?.name === 'pixelfed'; + return /pixelfed/i.test(instance?.version); } diff --git a/src/utils/supports.js b/src/utils/supports.js index 8721d925..07b1d54b 100644 --- a/src/utils/supports.js +++ b/src/utils/supports.js @@ -1,82 +1,57 @@ import { satisfies } from 'compare-versions'; +import features from '../data/features.json'; + import { getCurrentInstance } from './store-utils'; +// Non-semver(?) UA string detection +const containPixelfed = /pixelfed/i; +const notContainPixelfed = /^(?!.*pixelfed).*$/i; +const containPleroma = /pleroma/i; +const containAkkoma = /akkoma/i; +const containGTS = /gotosocial/i; const platformFeatures = { - '@mastodon/edit-media-attributes': [['mastodon', '>=4.1']], - '@mastodon/list-exclusive': [ - ['mastodon', '>=4.2'], - ['gotosocial', '>=0.17'], - ], - '@mastodon/filtered-notifications': [['mastodon', '>=4.3']], - '@mastodon/fetch-multiple-statuses': [['mastodon', '>=4.3']], - '@mastodon/trending-link-posts': [['mastodon', '>=4.3']], - '@mastodon/grouped-notifications': [['mastodon', '>=4.3']], - '@mastodon/lists': [['!pixelfed']], - '@mastodon/filters': [['!pixelfed']], - '@mastodon/mentions': [['!pixelfed']], - '@mastodon/trending-hashtags': [['!pixelfed']], - '@mastodon/trending-links': [['!pixelfed']], - '@mastodon/post-bookmark': [['!pixelfed']], - '@mastodon/post-edit': [['!pixelfed']], - '@mastodon/profile-edit': [['!pixelfed']], - '@mastodon/profile-private-note': [['!pixelfed']], - '@pixelfed/trending': [['pixelfed']], - '@pixelfed/home-include-reblogs': [['pixelfed']], - '@pixelfed/global-feed': [['pixelfed']], - '@pleroma/local-visibility-post': [['pleroma']], - '@akkoma/local-visibility-post': [['akkoma']], + '@mastodon/lists': notContainPixelfed, + '@mastodon/filters': notContainPixelfed, + '@mastodon/mentions': notContainPixelfed, + '@mastodon/trending-hashtags': notContainPixelfed, + '@mastodon/trending-links': notContainPixelfed, + '@mastodon/post-bookmark': notContainPixelfed, + '@mastodon/post-edit': notContainPixelfed, + '@mastodon/profile-edit': notContainPixelfed, + '@mastodon/profile-private-note': notContainPixelfed, + '@pixelfed/trending': containPixelfed, + '@pixelfed/home-include-reblogs': containPixelfed, + '@pixelfed/global-feed': containPixelfed, + '@pleroma/local-visibility-post': containPleroma, + '@akkoma/local-visibility-post': containAkkoma, }; const supportsCache = {}; function supports(feature) { - const specs = platformFeatures[feature]; - if (!specs) return false; - try { - let { version, domain, nodeInfo } = getCurrentInstance(); + let { version, domain, software_name } = getCurrentInstance(); const key = `${domain}-${feature}`; if (supportsCache[key]) return supportsCache[key]; - let software = 'mastodon'; - if ( - nodeInfo && nodeInfo.software && typeof nodeInfo.software.version === 'string' - && typeof nodeInfo.software.name === 'string' - ) { - software = nodeInfo.software.name.toLowerCase(); - version = nodeInfo.software.version; + if (platformFeatures[feature]) { + return (supportsCache[key] = platformFeatures[feature].test(version)); } - const isSupported = specs.some((spec) => versionSatisfies(software, version, spec)); - return (supportsCache[key] = isSupported); + const range = features[feature]; + if (!range) return false; + return (supportsCache[key] = ( + containGTS.test(feature) === containGTS.test(software_name) + && satisfies(version, range, { + includePrerelease: true, + loose: true, + }) + )); } catch (e) { return false; } } -function versionSatisfies(software, version, [softwareSpec, versionSpec]) { - let softwareMatches; - - // Inverted spec, like !pixelfed - if (softwareSpec.startsWith('!')) { - softwareMatches = software !== softwareSpec.slice(1); - } else { - softwareMatches = ( - software === softwareSpec || ( - // Hometown inherits Mastodon features - software === 'hometown' && softwareSpec === 'mastodon' - ) - ); - } - - return softwareMatches && ( - versionSpec == null || satisfies(version, versionSpec, { - includePrerelease: true, - loose: true, - }) - ); -} - export default supports;