diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json
index cce49de186..2ed08a8525 100644
--- a/apps/admin-x-activitypub/package.json
+++ b/apps/admin-x-activitypub/package.json
@@ -1,6 +1,6 @@
{
"name": "@tryghost/admin-x-activitypub",
- "version": "0.3.58",
+ "version": "0.3.59",
"license": "MIT",
"repository": {
"type": "git",
diff --git a/apps/admin-x-activitypub/src/MainContent.tsx b/apps/admin-x-activitypub/src/MainContent.tsx
index 43fe5b59d0..00abb54af9 100644
--- a/apps/admin-x-activitypub/src/MainContent.tsx
+++ b/apps/admin-x-activitypub/src/MainContent.tsx
@@ -1,5 +1,5 @@
-import Activities from './components/Activities';
import Inbox from './components/Inbox';
+import Notifications from './components/Notifications';
import Profile from './components/Profile';
import Search from './components/Search';
import {useRouting} from '@tryghost/admin-x-framework/routing';
@@ -10,8 +10,8 @@ const MainContent = () => {
switch (mainRoute) {
case 'search':
return ;
- case 'activity':
- return ;
+ case 'notifications':
+ return ;
case 'profile':
return ;
default:
diff --git a/apps/admin-x-activitypub/src/components/Activities.tsx b/apps/admin-x-activitypub/src/components/Notifications.tsx
similarity index 85%
rename from apps/admin-x-activitypub/src/components/Activities.tsx
rename to apps/admin-x-activitypub/src/components/Notifications.tsx
index b626bb85fc..0aa5628d3a 100644
--- a/apps/admin-x-activitypub/src/components/Activities.tsx
+++ b/apps/admin-x-activitypub/src/components/Notifications.tsx
@@ -21,13 +21,14 @@ import {
import {type NotificationType} from './activities/NotificationIcon';
import {handleProfileClick} from '../utils/handle-profile-click';
-interface ActivitiesProps {}
+interface NotificationsProps {}
// eslint-disable-next-line no-shadow
enum ACTIVITY_TYPE {
CREATE = 'Create',
LIKE = 'Like',
- FOLLOW = 'Follow'
+ FOLLOW = 'Follow',
+ REPOST = 'Announce'
}
interface GroupedActivity {
@@ -37,26 +38,9 @@ interface GroupedActivity {
id?: string;
}
-const getExtendedDescription = (activity: GroupedActivity): JSX.Element | null => {
- // If the activity is a reply
- if (Boolean(activity.type === ACTIVITY_TYPE.CREATE && activity.object?.inReplyTo)) {
- return (
-
- );
- } else if (activity.type === ACTIVITY_TYPE.LIKE && !activity.object?.name && activity.object?.content) {
- return (
-
- );
- }
-
- return null;
-};
+interface NotificationGroupDescriptionProps {
+ group: GroupedActivity;
+}
const getActivityBadge = (activity: GroupedActivity): NotificationType => {
switch (activity.type) {
@@ -65,12 +49,10 @@ const getActivityBadge = (activity: GroupedActivity): NotificationType => {
case ACTIVITY_TYPE.FOLLOW:
return 'follow';
case ACTIVITY_TYPE.LIKE:
- if (activity.object) {
- return 'like';
- }
+ return 'like';
+ case ACTIVITY_TYPE.REPOST:
+ return 'repost';
}
-
- return 'like';
};
const groupActivities = (activities: Activity[]): GroupedActivity[] => {
@@ -91,6 +73,12 @@ const groupActivities = (activities: Activity[]): GroupedActivity[] => {
groupKey = `like_${activity.object.id}`;
}
break;
+ case ACTIVITY_TYPE.REPOST:
+ if (activity.object?.id) {
+ // Group reposts by the target object
+ groupKey = `announce_${activity.object.id}`;
+ }
+ break;
case ACTIVITY_TYPE.CREATE:
// Don't group creates/replies
groupKey = `create_${activity.id}`;
@@ -116,7 +104,7 @@ const groupActivities = (activities: Activity[]): GroupedActivity[] => {
return Object.values(groups);
};
-const getGroupDescription = (group: GroupedActivity): JSX.Element => {
+const NotificationGroupDescription: React.FC = ({group}) => {
const [firstActor, secondActor, ...otherActors] = group.actors;
const hasOthers = otherActors.length > 0;
@@ -145,7 +133,9 @@ const getGroupDescription = (group: GroupedActivity): JSX.Element => {
case ACTIVITY_TYPE.FOLLOW:
return <>{actorText} started following you>;
case ACTIVITY_TYPE.LIKE:
- return <>{actorText} liked your post {group.object?.name || ''}>;
+ return <>{actorText} liked your {group.object?.type === 'Article' ? 'post' : 'note'} {group.object?.name || ''}>;
+ case ACTIVITY_TYPE.REPOST:
+ return <>{actorText} reposted your {group.object?.type === 'Article' ? 'post' : 'note'} {group.object?.name || ''}>;
case ACTIVITY_TYPE.CREATE:
if (group.object?.inReplyTo && typeof group.object?.inReplyTo !== 'string') {
let content = stripHtml(group.object.inReplyTo.content || '');
@@ -162,7 +152,7 @@ const getGroupDescription = (group: GroupedActivity): JSX.Element => {
return <>>;
};
-const Activities: React.FC = ({}) => {
+const Notifications: React.FC = () => {
const user = 'index';
const [openStates, setOpenStates] = React.useState<{[key: string]: boolean}>({});
@@ -183,7 +173,7 @@ const Activities: React.FC = ({}) => {
includeOwn: true,
includeReplies: true,
filter: {
- type: ['Follow', 'Like', `Create:Note`]
+ type: ['Follow', 'Like', `Create:Note`, `Announce:Note`, `Announce:Article`]
},
limit: 120,
key: GET_ACTIVITIES_QUERY_KEY_NOTIFICATIONS
@@ -215,6 +205,14 @@ const Activities: React.FC = ({}) => {
return true;
})
+ // Remove reposts that are not for our own posts
+ .filter((activity) => {
+ if (activity.type === ACTIVITY_TYPE.REPOST && activity.object?.attributedTo?.id !== userProfile?.id) {
+ return false;
+ }
+
+ return true;
+ })
// Remove create activities that are not replies to our own posts
.filter((activity) => {
if (
@@ -292,6 +290,14 @@ const Activities: React.FC = ({}) => {
handleProfileClick(group.actors[0]);
}
break;
+ case ACTIVITY_TYPE.REPOST:
+ NiceModal.show(ArticleModal, {
+ activityId: group.id,
+ object: group.object,
+ actor: group.object.attributedTo as ActorProperties,
+ width: group.object?.type === 'Article' ? 'wide' : 'narrow'
+ });
+ break;
}
};
@@ -377,9 +383,18 @@ const Activities: React.FC = ({}) => {
- {getGroupDescription(group)}
+
- {getExtendedDescription(group)}
+ {(
+ (group.type === ACTIVITY_TYPE.CREATE && group.object?.inReplyTo) ||
+ (group.type === ACTIVITY_TYPE.LIKE && !group.object?.name && group.object?.content) ||
+ (group.type === ACTIVITY_TYPE.REPOST && !group.object?.name && group.object?.content)
+ ) && (
+
+ )}
{index < groupedActivities.length - 1 && }
@@ -400,4 +415,4 @@ const Activities: React.FC = ({}) => {
);
};
-export default Activities;
+export default Notifications;
diff --git a/apps/admin-x-activitypub/src/components/activities/NotificationIcon.tsx b/apps/admin-x-activitypub/src/components/activities/NotificationIcon.tsx
index dc4a73e4bf..687caa72b3 100644
--- a/apps/admin-x-activitypub/src/components/activities/NotificationIcon.tsx
+++ b/apps/admin-x-activitypub/src/components/activities/NotificationIcon.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {Icon} from '@tryghost/admin-x-design-system';
-export type NotificationType = 'like' | 'follow' | 'reply';
+export type NotificationType = 'like' | 'follow' | 'reply' | 'repost';
interface NotificationIconProps {
notificationType: NotificationType;
@@ -29,6 +29,11 @@ const NotificationIcon: React.FC = ({notificationType, cl
iconColor = 'text-purple-500';
badgeColor = 'bg-purple-100/50';
break;
+ case 'repost':
+ icon = 'reload';
+ iconColor = 'text-green-500';
+ badgeColor = 'bg-green-100/50';
+ break;
}
return (
diff --git a/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx b/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx
index 21e783a333..eaa84d48e0 100644
--- a/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx
+++ b/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx
@@ -25,9 +25,24 @@ const MainNavigation: React.FC = ({page}) => {
unstyled
onClick={() => updateRoute('feed')}
/>
-