0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Fixed followers list on profile in admin-x-activitypub app (#21370)

refs
[AP-489](https://linear.app/ghost/issue/AP-489/followers-showing-unknown-on-user-profile)

Fixed the followers list on profile in admin-x-activitypub app by
utilising a custom endpoint to fetch a list of expanded followers
seeming though the followers endpoint only returns follower id's
This commit is contained in:
Michael Barrett 2024-10-22 20:21:12 +01:00 committed by GitHub
parent b9768f99e9
commit e9914d8fe5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 127 additions and 3 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@tryghost/admin-x-activitypub",
"version": "0.1.5",
"version": "0.1.6",
"license": "MIT",
"repository": {
"type": "git",

View file

@ -562,6 +562,104 @@ describe('ActivityPubAPI', function () {
});
});
describe('getFollowersExpanded', function () {
test('It passes the token to the followers endpoint', async function () {
const fakeFetch = Fetch({
'https://auth.api/': {
response: JSONResponse({
identities: [{
token: 'fake-token'
}]
})
},
'https://activitypub.api/.ghost/activitypub/followers-expanded/index': {
async assert(_resource, init) {
const headers = new Headers(init?.headers);
expect(headers.get('Authorization')).toContain('fake-token');
},
response: JSONResponse({
type: 'Collection',
orderedItems: []
})
}
});
const api = new ActivityPubAPI(
new URL('https://activitypub.api'),
new URL('https://auth.api'),
'index',
fakeFetch
);
await api.getFollowersExpanded();
});
test('Returns an empty array when the followers is empty', async function () {
const fakeFetch = Fetch({
'https://auth.api/': {
response: JSONResponse({
identities: [{
token: 'fake-token'
}]
})
},
'https://activitypub.api/.ghost/activitypub/followers-expanded/index': {
response: JSONResponse({
type: 'Collection',
orderedItems: []
})
}
});
const api = new ActivityPubAPI(
new URL('https://activitypub.api'),
new URL('https://auth.api'),
'index',
fakeFetch
);
const actual = await api.getFollowersExpanded();
const expected: never[] = [];
expect(actual).toEqual(expected);
});
test('Returns all the items array when the followers is not empty', async function () {
const fakeFetch = Fetch({
'https://auth.api/': {
response: JSONResponse({
identities: [{
token: 'fake-token'
}]
})
},
'https://activitypub.api/.ghost/activitypub/followers-expanded/index': {
response:
JSONResponse({
type: 'Collection',
orderedItems: [{
type: 'Person'
}]
})
}
});
const api = new ActivityPubAPI(
new URL('https://activitypub.api'),
new URL('https://auth.api'),
'index',
fakeFetch
);
const actual = await api.getFollowersExpanded();
const expected: Activity[] = [
{
type: 'Person'
}
];
expect(actual).toEqual(expected);
});
});
describe('follow', function () {
test('It passes the token to the follow endpoint', async function () {
const fakeFetch = Fetch({

View file

@ -159,6 +159,21 @@ export class ActivityPubAPI {
return 0;
}
get followersExpandedApiUrl() {
return new URL(`.ghost/activitypub/followers-expanded/${this.handle}`, this.apiUrl);
}
async getFollowersExpanded(): Promise<Activity[]> {
const json = await this.fetchJSON(this.followersExpandedApiUrl);
if (json === null) {
return [];
}
if ('orderedItems' in json) {
return Array.isArray(json.orderedItems) ? json.orderedItems : [json.orderedItems];
}
return [];
}
async getFollowersForProfile(handle: string, next?: string): Promise<GetFollowersForProfileResponse> {
const url = new URL(`.ghost/activitypub/profile/${handle}/followers`, this.apiUrl);
if (next) {

View file

@ -8,7 +8,7 @@ import {ActorProperties} from '@tryghost/admin-x-framework/api/activitypub';
import {Button, Heading, List, NoValueLabel, Tab, TabView} from '@tryghost/admin-x-design-system';
import {
useFollowersCountForUser,
useFollowersForUser,
useFollowersExpandedForUser,
useFollowingCountForUser,
useFollowingForUser,
useLikedForUser,
@ -22,7 +22,7 @@ const Profile: React.FC<ProfileProps> = ({}) => {
const {data: followersCount = 0} = useFollowersCountForUser('index');
const {data: followingCount = 0} = useFollowingCountForUser('index');
const {data: following = []} = useFollowingForUser('index');
const {data: followers = []} = useFollowersForUser('index');
const {data: followers = []} = useFollowersExpandedForUser('index');
const {data: liked = []} = useLikedForUser('index');
const {data: posts = []} = useOutboxForUser('index');

View file

@ -193,6 +193,17 @@ export function useFollowersForUser(handle: string) {
});
}
export function useFollowersExpandedForUser(handle: string) {
return useQuery({
queryKey: [`followers_expanded:${handle}`],
async queryFn() {
const siteUrl = await getSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
return api.getFollowersExpanded();
}
});
}
export function useAllActivitiesForUser({
handle,
includeOwn = false,