mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Fixed suggested accounts not rendering in admin-x-activitypub (#21655)
refs [AP-606](https://linear.app/ghost/issue/AP-606/suggested-accounts-not-rendering-due-to-404) Some of the suggested accounts in `admin-x-activitypub` were not rendering due to the requests for the data returning a 404. This was due to incorrect account handles being used. This commit fixes the issue by ensuring the correct handles are being used. This commit also adds a new hook to handle the fetching of suggested accounts that also limits and randomizes the accounts returned.
This commit is contained in:
parent
46bdbaa3b8
commit
2e9dbd3ef0
5 changed files with 113 additions and 15 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@tryghost/admin-x-activitypub",
|
||||
"version": "0.3.20",
|
||||
"version": "0.3.21",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -9,9 +9,10 @@ import Separator from './global/Separator';
|
|||
import ViewProfileModal from './global/ViewProfileModal';
|
||||
import getName from '../utils/get-name';
|
||||
import getUsername from '../utils/get-username';
|
||||
import useSuggestedProfiles from '../hooks/useSuggestedProfiles';
|
||||
import {Button, Heading, LoadingIndicator} from '@tryghost/admin-x-design-system';
|
||||
import {handleViewContent} from '../utils/content-handlers';
|
||||
import {useActivitiesForUser, useSuggestedProfiles} from '../hooks/useActivityPubQueries';
|
||||
import {useActivitiesForUser} from '../hooks/useActivityPubQueries';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
type Layout = 'inbox' | 'feed';
|
||||
|
@ -22,7 +23,7 @@ interface InboxProps {
|
|||
|
||||
const Inbox: React.FC<InboxProps> = ({layout}) => {
|
||||
const typeFilter = layout === 'inbox'
|
||||
? ['Create:Article']
|
||||
? ['Create:Article']
|
||||
: ['Create:Note', 'Announce:Note'];
|
||||
|
||||
const {getActivitiesQuery, updateActivity} = useActivitiesForUser({
|
||||
|
@ -37,8 +38,7 @@ const Inbox: React.FC<InboxProps> = ({layout}) => {
|
|||
|
||||
const {updateRoute} = useRouting();
|
||||
|
||||
const {suggestedProfilesQuery} = useSuggestedProfiles('index', ['@index@activitypub.ghost.org', '@index@john.onolan.org', '@index@coffeecomplex.ghost.io', '@index@codename-jimmy.ghost.io', '@index@syphoncontinuity.ghost.io']);
|
||||
const {data: suggested = [], isLoading: isLoadingSuggested} = suggestedProfilesQuery;
|
||||
const {suggested, isLoadingSuggested} = useSuggestedProfiles();
|
||||
|
||||
const activities = (data?.pages.flatMap(page => page.data) ?? []).filter((activity) => {
|
||||
return !activity.object.inReplyTo;
|
||||
|
@ -168,7 +168,7 @@ const Inbox: React.FC<InboxProps> = ({layout}) => {
|
|||
Welcome to ActivityPub Beta
|
||||
</Heading>
|
||||
<p className="text-pretty text-grey-800">
|
||||
{layout === 'inbox'
|
||||
{layout === 'inbox'
|
||||
? 'Here you\'ll find the latest articles from accounts you\'re following.'
|
||||
: 'Here you\'ll find the latest posts and updates from accounts you\'re following.'
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ import NiceModal from '@ebay/nice-modal-react';
|
|||
import ViewProfileModal from './global/ViewProfileModal';
|
||||
|
||||
import Separator from './global/Separator';
|
||||
import {useSearchForUser, useSuggestedProfiles} from '../hooks/useActivityPubQueries';
|
||||
import useSuggestedProfiles from '../hooks/useSuggestedProfiles';
|
||||
import {useSearchForUser} from '../hooks/useActivityPubQueries';
|
||||
|
||||
interface SearchResultItem {
|
||||
actor: ActorProperties;
|
||||
|
@ -122,8 +123,7 @@ const SuggestedAccounts: React.FC<{
|
|||
|
||||
const Search: React.FC<SearchProps> = ({}) => {
|
||||
// Initialise suggested profiles
|
||||
const {suggestedProfilesQuery, updateSuggestedProfile} = useSuggestedProfiles('index', ['@index@activitypub.ghost.org', '@index@john.onolan.org', '@index@coffeecomplex.ghost.io', '@index@codename-jimmy.ghost.io', '@index@syphoncontinuity.ghost.io']);
|
||||
const {data: suggested = [], isLoading: isLoadingSuggested} = suggestedProfilesQuery;
|
||||
const {suggested, isLoadingSuggested, updateSuggestedProfile} = useSuggestedProfiles(6);
|
||||
|
||||
// Initialise search query
|
||||
const queryInputRef = useRef<HTMLInputElement>(null);
|
||||
|
@ -178,22 +178,22 @@ const Search: React.FC<SearchProps> = ({}) => {
|
|||
)}
|
||||
</div>
|
||||
{showLoading && <LoadingIndicator size='lg'/>}
|
||||
|
||||
|
||||
{showNoResults && (
|
||||
<NoValueLabel icon='user'>
|
||||
No users matching this username
|
||||
</NoValueLabel>
|
||||
)}
|
||||
|
||||
|
||||
{!showLoading && !showNoResults && (
|
||||
<SearchResults
|
||||
results={results as SearchResultItem[]}
|
||||
<SearchResults
|
||||
results={results as SearchResultItem[]}
|
||||
onUpdate={updateResult}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{showSuggested && (
|
||||
<SuggestedAccounts
|
||||
<SuggestedAccounts
|
||||
isLoading={isLoadingSuggested}
|
||||
profiles={suggested as SearchResultItem[]}
|
||||
onUpdate={updateSuggestedProfile}
|
||||
|
|
27
apps/admin-x-activitypub/src/hooks/useSuggestedProfiles.ts
Normal file
27
apps/admin-x-activitypub/src/hooks/useSuggestedProfiles.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import {useMemo} from 'react';
|
||||
import {useSuggestedProfiles as useSuggestedProfilesQuery} from '../hooks/useActivityPubQueries';
|
||||
|
||||
export const SUGGESTED_HANDLES = [
|
||||
'@index@activitypub.ghost.org',
|
||||
'@index@john.onolan.org',
|
||||
'@index@www.coffeeandcomplexity.com',
|
||||
'@index@ghost.codenamejimmy.com',
|
||||
'@index@www.syphoncontinuity.com',
|
||||
'@index@www.cosmico.org',
|
||||
'@index@silverhuang.com'
|
||||
];
|
||||
|
||||
const useSuggestedProfiles = (limit = 3) => {
|
||||
const handles = useMemo(() => {
|
||||
return SUGGESTED_HANDLES
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, limit);
|
||||
}, [limit]);
|
||||
|
||||
const {suggestedProfilesQuery, updateSuggestedProfile} = useSuggestedProfilesQuery('index', handles);
|
||||
const {data: suggested = [], isLoading: isLoadingSuggested} = suggestedProfilesQuery;
|
||||
|
||||
return {suggested, isLoadingSuggested, updateSuggestedProfile};
|
||||
};
|
||||
|
||||
export default useSuggestedProfiles;
|
|
@ -0,0 +1,71 @@
|
|||
import {Mock, vi} from 'vitest';
|
||||
import {renderHook} from '@testing-library/react';
|
||||
import type {UseQueryResult} from '@tanstack/react-query';
|
||||
|
||||
import * as useActivityPubQueries from '../../../src/hooks/useActivityPubQueries';
|
||||
import useSuggestedProfiles, {SUGGESTED_HANDLES} from '../../../src/hooks/useSuggestedProfiles';
|
||||
import type{Profile} from '../../../src/api/activitypub';
|
||||
|
||||
vi.mock('../../../src/hooks/useActivityPubQueries');
|
||||
|
||||
describe('useSuggestedProfiles', function () {
|
||||
let mockUpdateSuggestedProfile: Mock;
|
||||
|
||||
beforeEach(function () {
|
||||
mockUpdateSuggestedProfile = vi.fn();
|
||||
|
||||
vi.mocked(useActivityPubQueries.useSuggestedProfiles).mockImplementation((handle, handles) => {
|
||||
// We expect the handle to be 'index', throw if anything else
|
||||
if (handle !== 'index') {
|
||||
throw new Error(`Expected handle to be: [index], got: [${handle}]`);
|
||||
}
|
||||
|
||||
// Return the mocked query result making sure that the data is the
|
||||
// same as the handles passed in. For the purposes of this test,
|
||||
// we don't need to test the internals of useSuggestedProfilesQuery
|
||||
return {
|
||||
suggestedProfilesQuery: {
|
||||
data: handles,
|
||||
isLoading: false
|
||||
} as unknown as UseQueryResult<Profile[], unknown>,
|
||||
updateSuggestedProfile: mockUpdateSuggestedProfile
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the default number of suggested profiles', function () {
|
||||
const {result} = renderHook(() => useSuggestedProfiles());
|
||||
|
||||
// Check that the correct number of suggested profiles are returned
|
||||
expect(result.current.suggested.length).toEqual(3);
|
||||
|
||||
// Check that the suggested profiles are in the SUGGESTED_HANDLES array
|
||||
result.current.suggested.forEach((suggested) => {
|
||||
expect(SUGGESTED_HANDLES).toContain(suggested);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the specified number of suggested profiles', function () {
|
||||
const {result} = renderHook(() => useSuggestedProfiles(5));
|
||||
|
||||
// Assert that the correct number of suggested profiles are returned
|
||||
expect(result.current.suggested.length).toEqual(5);
|
||||
|
||||
// Assert that the suggested profiles are in the SUGGESTED_HANDLES array
|
||||
result.current.suggested.forEach((suggested) => {
|
||||
expect(SUGGESTED_HANDLES).toContain(suggested);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a loading state', function () {
|
||||
const {result} = renderHook(() => useSuggestedProfiles());
|
||||
|
||||
expect(result.current.isLoadingSuggested).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return a function to update a suggested profile', function () {
|
||||
const {result} = renderHook(() => useSuggestedProfiles());
|
||||
|
||||
expect(result.current.updateSuggestedProfile).toEqual(mockUpdateSuggestedProfile);
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue