mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added posts to the user profile in admin-x-activitypub (#21369)
refs [AP-484](https://linear.app/ghost/issue/AP-484/render-posts-on-user-profile) Added posts to the user profile in admin-x-activitypub
This commit is contained in:
parent
febb7f720e
commit
b9768f99e9
4 changed files with 209 additions and 4 deletions
|
@ -182,6 +182,153 @@ describe('ActivityPubAPI', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getOutbox', function () {
|
||||
test('It passes the token to the outbox endpoint', async function () {
|
||||
const fakeFetch = Fetch({
|
||||
'https://auth.api/': {
|
||||
response: JSONResponse({
|
||||
identities: [{
|
||||
token: 'fake-token'
|
||||
}]
|
||||
})
|
||||
},
|
||||
'https://activitypub.api/.ghost/activitypub/outbox/index': {
|
||||
async assert(_resource, init) {
|
||||
const headers = new Headers(init?.headers);
|
||||
expect(headers.get('Authorization')).toContain('fake-token');
|
||||
},
|
||||
response: JSONResponse({
|
||||
type: 'Collection',
|
||||
items: []
|
||||
})
|
||||
}
|
||||
});
|
||||
const api = new ActivityPubAPI(
|
||||
new URL('https://activitypub.api'),
|
||||
new URL('https://auth.api'),
|
||||
'index',
|
||||
fakeFetch
|
||||
);
|
||||
|
||||
await api.getOutbox();
|
||||
});
|
||||
|
||||
test('Returns an empty array when the outbox is empty', async function () {
|
||||
const fakeFetch = Fetch({
|
||||
'https://auth.api/': {
|
||||
response: JSONResponse({
|
||||
identities: [{
|
||||
token: 'fake-token'
|
||||
}]
|
||||
})
|
||||
},
|
||||
'https://activitypub.api/.ghost/activitypub/outbox/index': {
|
||||
response: JSONResponse({
|
||||
type: 'Collection',
|
||||
items: []
|
||||
})
|
||||
}
|
||||
});
|
||||
const api = new ActivityPubAPI(
|
||||
new URL('https://activitypub.api'),
|
||||
new URL('https://auth.api'),
|
||||
'index',
|
||||
fakeFetch
|
||||
);
|
||||
|
||||
const actual = await api.getOutbox();
|
||||
const expected: never[] = [];
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('Returns all the items array when the outbox is not empty', async function () {
|
||||
const fakeFetch = Fetch({
|
||||
'https://auth.api/': {
|
||||
response: JSONResponse({
|
||||
identities: [{
|
||||
token: 'fake-token'
|
||||
}]
|
||||
})
|
||||
},
|
||||
'https://activitypub.api/.ghost/activitypub/outbox/index': {
|
||||
response:
|
||||
JSONResponse({
|
||||
type: 'Collection',
|
||||
orderedItems: [{
|
||||
type: 'Create',
|
||||
object: {
|
||||
type: 'Note'
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const api = new ActivityPubAPI(
|
||||
new URL('https://activitypub.api'),
|
||||
new URL('https://auth.api'),
|
||||
'index',
|
||||
fakeFetch
|
||||
);
|
||||
|
||||
const actual = await api.getOutbox();
|
||||
const expected: Activity[] = [
|
||||
{
|
||||
type: 'Create',
|
||||
object: {
|
||||
type: 'Note'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
test('Returns an array when the orderedItems key is a single object', async function () {
|
||||
const fakeFetch = Fetch({
|
||||
'https://auth.api/': {
|
||||
response: JSONResponse({
|
||||
identities: [{
|
||||
token: 'fake-token'
|
||||
}]
|
||||
})
|
||||
},
|
||||
'https://activitypub.api/.ghost/activitypub/outbox/index': {
|
||||
response:
|
||||
JSONResponse({
|
||||
type: 'Collection',
|
||||
orderedItems: {
|
||||
type: 'Create',
|
||||
object: {
|
||||
type: 'Note'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const api = new ActivityPubAPI(
|
||||
new URL('https://activitypub.api'),
|
||||
new URL('https://auth.api'),
|
||||
'index',
|
||||
fakeFetch
|
||||
);
|
||||
|
||||
const actual = await api.getOutbox();
|
||||
const expected: Activity[] = [
|
||||
{
|
||||
type: 'Create',
|
||||
object: {
|
||||
type: 'Note'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFollowing', function () {
|
||||
test('It passes the token to the following endpoint', async function () {
|
||||
const fakeFetch = Fetch({
|
||||
|
|
|
@ -86,6 +86,24 @@ export class ActivityPubAPI {
|
|||
return [];
|
||||
}
|
||||
|
||||
get outboxApiUrl() {
|
||||
return new URL(`.ghost/activitypub/outbox/${this.handle}`, this.apiUrl);
|
||||
}
|
||||
|
||||
async getOutbox(): Promise<Activity[]> {
|
||||
const json = await this.fetchJSON(this.outboxApiUrl);
|
||||
if (json === null) {
|
||||
return [];
|
||||
}
|
||||
if ('orderedItems' in json) {
|
||||
return Array.isArray(json.orderedItems) ? json.orderedItems : [json.orderedItems];
|
||||
}
|
||||
if ('items' in json) {
|
||||
return Array.isArray(json.items) ? json.items : [json.items];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
get followingApiUrl() {
|
||||
return new URL(`.ghost/activitypub/following/${this.handle}`, this.apiUrl);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
useFollowingCountForUser,
|
||||
useFollowingForUser,
|
||||
useLikedForUser,
|
||||
useOutboxForUser,
|
||||
useUserDataForUser
|
||||
} from '../hooks/useActivityPubQueries';
|
||||
|
||||
|
@ -23,6 +24,8 @@ const Profile: React.FC<ProfileProps> = ({}) => {
|
|||
const {data: following = []} = useFollowingForUser('index');
|
||||
const {data: followers = []} = useFollowersForUser('index');
|
||||
const {data: liked = []} = useLikedForUser('index');
|
||||
const {data: posts = []} = useOutboxForUser('index');
|
||||
|
||||
// Replace 'index' with the actual handle of the user
|
||||
const {data: userProfile} = useUserDataForUser('index') as {data: ActorProperties | null};
|
||||
|
||||
|
@ -36,10 +39,36 @@ const Profile: React.FC<ProfileProps> = ({}) => {
|
|||
{
|
||||
id: 'posts',
|
||||
title: 'Posts',
|
||||
contents: (<div><NoValueLabel icon='pen'>
|
||||
You haven't posted anything yet.
|
||||
</NoValueLabel></div>),
|
||||
counter: 240
|
||||
contents: (
|
||||
<div className='ap-posts'>
|
||||
{posts.length === 0 ? (
|
||||
<NoValueLabel icon='pen'>
|
||||
You haven't posted anything yet.
|
||||
</NoValueLabel>
|
||||
) : (
|
||||
<ul className='mx-auto flex max-w-[640px] flex-col'>
|
||||
{posts.map((activity, index) => (
|
||||
<li
|
||||
key={activity.id}
|
||||
data-test-view-article
|
||||
>
|
||||
<FeedItem
|
||||
actor={activity.object?.attributedTo || activity.actor}
|
||||
layout={layout}
|
||||
object={activity.object}
|
||||
type={activity.type}
|
||||
onCommentClick={() => {}}
|
||||
/>
|
||||
{index < posts.length - 1 && (
|
||||
<div className="h-px w-full bg-grey-200"></div>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
counter: posts.length
|
||||
},
|
||||
{
|
||||
id: 'likes',
|
||||
|
|
|
@ -362,3 +362,14 @@ export function useProfileForUser(handle: string, fullHandle: string) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function useOutboxForUser(handle: string) {
|
||||
return useQuery({
|
||||
queryKey: [`outbox:${handle}`],
|
||||
async queryFn() {
|
||||
const siteUrl = await getSiteUrl();
|
||||
const api = createActivityPubAPI(handle, siteUrl);
|
||||
return api.getOutbox();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue