diff --git a/src/components/list-add-edit.jsx b/src/components/list-add-edit.jsx
index d9a21a4b..17279b7b 100644
--- a/src/components/list-add-edit.jsx
+++ b/src/components/list-add-edit.jsx
@@ -24,7 +24,9 @@ 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
index 6ff4bb1e..7376418b 100644
--- a/src/data/features.json
+++ b/src/data/features.json
@@ -1,6 +1,7 @@
{
"@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",
diff --git a/src/locales/en.po b/src/locales/en.po
index f32729e4..2ae08b2e 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -408,7 +408,7 @@ msgstr ""
#: src/components/embed-modal.jsx:12
#: src/components/generic-accounts.jsx:142
#: src/components/keyboard-shortcuts-help.jsx:39
-#: src/components/list-add-edit.jsx:33
+#: src/components/list-add-edit.jsx:35
#: src/components/media-alt-modal.jsx:33
#: src/components/media-modal.jsx:352
#: src/components/notification-service.jsx:156
@@ -452,7 +452,7 @@ msgid "No lists."
msgstr ""
#: src/components/account-info.jsx:1939
-#: src/components/list-add-edit.jsx:37
+#: src/components/list-add-edit.jsx:39
#: src/pages/lists.jsx:58
msgid "New list"
msgstr ""
@@ -479,7 +479,7 @@ msgid "Unable to update profile."
msgstr ""
#: src/components/account-info.jsx:2155
-#: src/components/list-add-edit.jsx:102
+#: src/components/list-add-edit.jsx:104
msgid "Name"
msgstr ""
@@ -500,7 +500,7 @@ msgid "Content"
msgstr ""
#: src/components/account-info.jsx:2223
-#: src/components/list-add-edit.jsx:147
+#: src/components/list-add-edit.jsx:149
#: src/components/shortcuts-settings.jsx:715
#: src/pages/filters.jsx:554
#: src/pages/notifications.jsx:934
@@ -891,7 +891,7 @@ msgid "Error deleting draft! Please try again."
msgstr ""
#: src/components/drafts.jsx:127
-#: src/components/list-add-edit.jsx:183
+#: src/components/list-add-edit.jsx:185
#: src/components/status.jsx:1336
#: src/pages/filters.jsx:587
msgid "Deleteā¦"
@@ -1126,44 +1126,44 @@ msgstr ""
msgid "<0>Shift0> + <1>Alt1> + <2>k2>"
msgstr ""
-#: src/components/list-add-edit.jsx:37
+#: src/components/list-add-edit.jsx:39
msgid "Edit list"
msgstr ""
-#: src/components/list-add-edit.jsx:93
+#: src/components/list-add-edit.jsx:95
msgid "Unable to edit list."
msgstr ""
-#: src/components/list-add-edit.jsx:94
+#: src/components/list-add-edit.jsx:96
msgid "Unable to create list."
msgstr ""
-#: src/components/list-add-edit.jsx:122
+#: src/components/list-add-edit.jsx:124
msgid "Show replies to list members"
msgstr ""
-#: src/components/list-add-edit.jsx:125
+#: src/components/list-add-edit.jsx:127
msgid "Show replies to people I follow"
msgstr ""
-#: src/components/list-add-edit.jsx:128
+#: src/components/list-add-edit.jsx:130
msgid "Don't show replies"
msgstr ""
-#: src/components/list-add-edit.jsx:141
+#: src/components/list-add-edit.jsx:143
msgid "Hide posts on this list from Home/Following"
msgstr ""
-#: src/components/list-add-edit.jsx:147
+#: src/components/list-add-edit.jsx:149
#: src/pages/filters.jsx:554
msgid "Create"
msgstr ""
-#: src/components/list-add-edit.jsx:154
+#: src/components/list-add-edit.jsx:156
msgid "Delete this list?"
msgstr ""
-#: src/components/list-add-edit.jsx:173
+#: src/components/list-add-edit.jsx:175
msgid "Unable to delete list."
msgstr ""
diff --git a/src/utils/api.js b/src/utils/api.js
index cf6652bc..7c311e4c 100644
--- a/src/utils/api.js
+++ b/src/utils/api.js
@@ -89,6 +89,7 @@ export async function initInstance(client, instance) {
domain,
configuration: { urls: { streaming } = {} } = {},
} = info;
+
const instances = store.local.getJSON('instances') || {};
if (uri || domain) {
instances[
@@ -102,6 +103,34 @@ export async function initInstance(client, instance) {
instances[instance.toLowerCase()] = info;
}
store.local.setJSON('instances', instances);
+
+ 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 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)) {
+ nodeInfo = await (await fetch(nodeInfoUrl)).json();
+ }
+ }
+ }
+ } catch (e) {}
+ const nodeInfos = store.local.getJSON('nodeInfos') || {};
+ if (nodeInfo) {
+ nodeInfos[instance.toLowerCase()] = nodeInfo;
+ }
+ store.local.setJSON('nodeInfos', nodeInfos);
+
// This is a weird place to put this but here's updating the masto instance with the streaming API URL set in the configuration
// Reason: Streaming WebSocket URL may change, unlike the standard API REST URLs
const supportsWebSocket = 'WebSocket' in window;
diff --git a/src/utils/store-utils.js b/src/utils/store-utils.js
index 5f8fcd73..02fd38b2 100644
--- a/src/utils/store-utils.js
+++ b/src/utils/store-utils.js
@@ -115,6 +115,20 @@ export function getCurrentInstance() {
}
}
+let currentNodeInfo = null;
+export function getCurrentNodeInfo() {
+ if (currentNodeInfo) return currentNodeInfo;
+ try {
+ const account = getCurrentAccount();
+ const nodeInfos = store.local.getJSON('nodeInfos') || {};
+ const instanceURL = account.instanceURL.toLowerCase();
+ return (currentNodeInfo = nodeInfos[instanceURL] || {});
+ } catch (e) {
+ console.error(e);
+ return {};
+ }
+}
+
// Massage these instance configurations to match the Mastodon API
// - Pleroma
function getInstanceConfiguration(instance) {
diff --git a/src/utils/supports.js b/src/utils/supports.js
index 66454224..e240037a 100644
--- a/src/utils/supports.js
+++ b/src/utils/supports.js
@@ -2,13 +2,14 @@ import { satisfies } from 'compare-versions';
import features from '../data/features.json';
-import { getCurrentInstance } from './store-utils';
+import { getCurrentInstance, getCurrentNodeInfo } 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/lists': notContainPixelfed,
'@mastodon/filters': notContainPixelfed,
@@ -25,11 +26,19 @@ const platformFeatures = {
'@pleroma/local-visibility-post': containPleroma,
'@akkoma/local-visibility-post': containAkkoma,
};
+
const supportsCache = {};
function supports(feature) {
try {
- const { version, domain } = getCurrentInstance();
+ let { version, domain } = getCurrentInstance();
+ let softwareName = getCurrentNodeInfo()?.software?.name || 'mastodon';
+
+ if (softwareName === 'hometown') {
+ // Hometown is a Mastodon fork and inherits its features
+ softwareName = 'mastodon';
+ }
+
const key = `${domain}-${feature}`;
if (supportsCache[key]) return supportsCache[key];
@@ -39,10 +48,17 @@ function supports(feature) {
const range = features[feature];
if (!range) return false;
- return (supportsCache[key] = satisfies(version, range, {
- includePrerelease: true,
- loose: true,
- }));
+
+ // '@mastodon/blah' => 'mastodon'
+ const featureSoftware = feature.match(/^@([a-z]+)\//)[1];
+
+ const doesSoftwareMatch = featureSoftware === softwareName.toLowerCase();
+ return (supportsCache[key] =
+ doesSoftwareMatch &&
+ satisfies(version, range, {
+ includePrerelease: true,
+ loose: true,
+ }));
} catch (e) {
return false;
}