Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support exclusive lists with GoToSocial 0.17 #817

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/list-add-edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div class="sheet">
Expand Down
1 change: 1 addition & 0 deletions src/data/features.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
30 changes: 15 additions & 15 deletions src/locales/en.po

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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[
Expand All @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions src/utils/store-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
28 changes: 22 additions & 6 deletions src/utils/supports.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering, how is this relevant to the current PR, which is meant for GTS?

Copy link
Contributor Author

@graue graue Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a future Hometown release includes the fix for nodeInfo, softwareName will be 'hometown', so for features like '@mastodon/list-exclusive', without the above line of code, this comparison would be false ('mastodon' === 'hometown'):

const doesSoftwareMatch = featureSoftware === softwareName.toLowerCase();

softwareName = 'mastodon';
}

const key = `${domain}-${feature}`;
if (supportsCache[key]) return supportsCache[key];

Expand All @@ -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];
Copy link
Owner

@cheeaun cheeaun Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be like (feature.match(/^@([a-z]+)\//) || [,false])[1] because if there's no match, match will return null.

I know my code above looks fancy (it's in other files too) so feel free to write it in a more understandable way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I could rely on supports() only being called with strings that match the @software/feature-name pattern; currently, this is the case:

$ git grep supports\(
src/components/account-info.jsx:                {supports('@mastodon/profile-private-note') && (
src/components/account-info.jsx:              supports('@mastodon/profile-edit') && (
src/components/compose.jsx:                if (editStatus && supports('@mastodon/edit-media-attributes')) {
src/components/compose.jsx:                {(supports('@pleroma/local-visibility-post') ||
src/components/compose.jsx:                  supports('@akkoma/local-visibility-post')) && (
src/components/compose.jsx:  const supportsEdit = supports('@mastodon/edit-media-attributes');
... etc., all of the calls are like this

So it seems like a call which did not match that pattern would likely be a bug. Do you want me to check for that here anyway?


const doesSoftwareMatch = featureSoftware === softwareName.toLowerCase();
return (supportsCache[key] =
doesSoftwareMatch &&
satisfies(version, range, {
includePrerelease: true,
loose: true,
}));
} catch (e) {
return false;
}
Expand Down