Skip to content
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
2 changes: 1 addition & 1 deletion apps/activitypub/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryghost/activitypub",
"version": "2.0.2",
"version": "2.0.3",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
2 changes: 2 additions & 0 deletions apps/activitypub/src/api/activitypub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export interface Notification {
url: string;
handle: string;
avatarUrl: string | null;
followedByMe?: boolean;
followsMe?: boolean;
},
post: null | {
id: string;
Expand Down
31 changes: 30 additions & 1 deletion apps/activitypub/src/components/global/FollowButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface FollowButtonProps {
following: boolean;
handle: string;
type?: 'primary' | 'secondary';
variant?: 'default' | 'link';
followsYou?: boolean;
onFollow?: () => void;
onUnfollow?: () => void;
'data-testid'?: string;
Expand All @@ -19,6 +21,8 @@ const FollowButton: React.FC<FollowButtonProps> = ({
className,
following,
handle,
variant = 'default',
followsYou = false,
onFollow = noop,
onUnfollow = noop,
'data-testid': testId
Expand Down Expand Up @@ -59,6 +63,31 @@ const FollowButton: React.FC<FollowButtonProps> = ({
setIsFollowing(following);
}, [following]);

const buttonText = isFollowing ? 'Following' : (followsYou ? 'Follow back' : 'Follow');

if (variant === 'link') {
return (
<Button
className={clsx(
'p-0 font-medium',
isFollowing
? 'text-gray-700 hover:text-black dark:text-gray-600 dark:hover:text-white'
: 'text-purple hover:text-black dark:hover:text-white',
className
)}
data-testid={testId}
variant="link"
onClick={(event) => {
event?.preventDefault();
event?.stopPropagation();
handleClick();
}}
>
{buttonText}
</Button>
);
}

return (
<Button
className={clsx(
Expand All @@ -74,7 +103,7 @@ const FollowButton: React.FC<FollowButtonProps> = ({
handleClick();
}}
>
{isFollowing ? 'Following' : 'Follow'}
{buttonText}
</Button>
);
};
Expand Down
35 changes: 22 additions & 13 deletions apps/activitypub/src/views/Inbox/components/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import BackButton from '@src/components/global/BackButton';
import DeletedFeedItem from '@src/components/feed/DeletedFeedItem';
import FeedItem from '@src/components/feed/FeedItem';
import FeedItemStats from '@src/components/feed/FeedItemStats';
import FollowButton from '@src/components/global/FollowButton';
import ProfilePreviewHoverCard from '@components/global/ProfilePreviewHoverCard';
import TableOfContents, {TOCItem} from '@src/components/feed/TableOfContents';
import articleBodyStyles from '@src/components/articleBodyStyles';
Expand Down Expand Up @@ -807,22 +808,30 @@ export const Reader: React.FC<ReaderProps> = ({
<div className='flex items-center'>
<BackButton className={COLOR_OPTIONS[backgroundColor].button} onClick={onClose} />
</div>
<ProfilePreviewHoverCard actor={actor} isCurrentUser={object.authored}>
<div className='col-[2/3] mx-auto flex w-full cursor-pointer items-center gap-3 max-md:hidden'>
<div className='relative z-10 pt-0.5'>
<APAvatar author={actor}/>
</div>
<div className='relative z-10 mt-0.5 flex w-full min-w-0 cursor-pointer flex-col overflow-visible text-[1.5rem]' onClick={e => handleProfileClick(actor, navigate, e)}>
<div className='flex w-full'>
<span className='min-w-0 truncate whitespace-nowrap font-semibold text-black hover:underline dark:text-white'>{isLoadingContent ? <Skeleton className='w-20' /> : actor.name}</span>
<div className='col-[2/3] mx-auto flex w-full items-center justify-between gap-3 max-md:hidden'>
<ProfilePreviewHoverCard actor={actor} isCurrentUser={object.authored}>
<div className='flex cursor-pointer items-center gap-3'>
<div className='relative z-10 pt-0.5'>
<APAvatar author={actor}/>
</div>
<div className='flex w-full'>
{!isLoadingContent && <span className='truncate text-gray-700 after:mx-1 after:font-normal after:text-gray-700 after:content-["·"]'>{getUsername(actor)}</span>}
<span className='text-gray-700'>{isLoadingContent ? <Skeleton className='w-[120px]' /> : renderTimestamp(object, !object.authored)}</span>
<div className='relative z-10 mt-0.5 flex min-w-0 cursor-pointer flex-col overflow-visible text-[1.5rem]' onClick={e => handleProfileClick(actor, navigate, e)}>
<div className='flex w-full'>
<span className='min-w-0 truncate whitespace-nowrap font-semibold text-black hover:underline dark:text-white'>{isLoadingContent ? <Skeleton className='w-20' /> : actor.name}</span>
</div>
<div className='flex w-full'>
{!isLoadingContent && <span className='truncate text-gray-700 after:mx-1 after:font-normal after:text-gray-700 after:content-["·"]'>{getUsername(actor)}</span>}
<span className='text-gray-700'>{isLoadingContent ? <Skeleton className='w-[120px]' /> : renderTimestamp(object, !object.authored)}</span>
</div>
</div>
</div>
</div>
</ProfilePreviewHoverCard>
</ProfilePreviewHoverCard>
{!object.authored && !isLoadingContent && (
<FollowButton
following={actor.followedByMe ?? false}
handle={getUsername(actor)}
/>
)}
</div>
<div className='col-[3/4] flex items-center justify-end gap-2'>
<Customizer
backgroundColor={backgroundColor}
Expand Down
62 changes: 42 additions & 20 deletions apps/activitypub/src/views/Notifications/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Button, LoadingIndicator, LucideIcon, Skeleton} from '@tryghost/shade';
import APAvatar from '@components/global/APAvatar';
import Error from '@components/layout/Error';
import FeedItemStats from '@components/feed/FeedItemStats';
import FollowButton from '@components/global/FollowButton';
import Layout from '@components/layout';
import NotificationIcon from './components/NotificationIcon';
import NotificationItem from './components/NotificationItem';
Expand Down Expand Up @@ -118,7 +119,7 @@ const NotificationGroupDescription: React.FC<NotificationGroupDescriptionProps>

const actorText = (
<>
<ProfilePreviewHoverCard actor={firstActor as unknown as ActorProperties} isCurrentUser={false}>
<ProfilePreviewHoverCard actor={firstActor as unknown as ActorProperties} align="center" isCurrentUser={false}>
<span
className={actorClass}
onClick={(e) => {
Expand Down Expand Up @@ -329,7 +330,7 @@ const Notifications: React.FC = () => {
)
}
{group.actors.length > 1 && <NotificationItem.Avatars>
<div className='flex flex-col'>
<div className='flex w-full flex-col'>
<div className='relative flex items-center pl-2'>
{!openStates[group.id || `${group.type}_${index}`] && group.actors.slice(0, maxAvatars).map((actor: ActorProperties) => (
<APAvatar
Expand Down Expand Up @@ -367,21 +368,31 @@ const Notifications: React.FC = () => {
{group.actors.map((actor: ActorProperties) => (
<div
key={actor.id}
className='flex items-center break-anywhere hover:opacity-80'
className='group/item flex items-center justify-between gap-4 break-anywhere'
onClick={(e) => {
e?.stopPropagation();
handleProfileClick(actor.handle, navigate);
}}
>
<APAvatar author={{
icon: {
url: actor.avatarUrl || ''
},
name: actor.name,
handle: actor.handle
}} size='xs' />
<span className='ml-2 line-clamp-1 text-base font-semibold dark:text-white'>{actor.name}</span>
<span className='ml-1 line-clamp-1 text-base text-gray-700 dark:text-gray-600'>{actor.handle}</span>
<div className='flex min-w-0 items-center'>
<APAvatar author={{
icon: {
url: actor.avatarUrl || ''
},
name: actor.name,
handle: actor.handle
}} size='xs' />
<span className='ml-2 line-clamp-1 text-base font-semibold group-hover/item:underline dark:text-white'>{actor.name}</span>
<span className='ml-1 line-clamp-1 text-base text-gray-700 dark:text-gray-600'>{actor.handle}</span>
</div>
{group.type === 'follow' && !actor.followedByMe && (
<FollowButton
following={false}
followsYou={actor.followsMe}
handle={actor.handle}
variant="link"
/>
)}
</div>
))}
</div>
Expand All @@ -396,14 +407,25 @@ const Notifications: React.FC = () => {
<Skeleton />
<Skeleton className='w-full max-w-60' />
</> :
<div className='flex items-center gap-1'>
<span className='truncate'><NotificationGroupDescription group={group} /></span>
{group.actors.length < 2 &&
<>
<span className='mt-px text-[8px] text-gray-700 dark:text-gray-600'>&bull;</span>
<span className='mt-0.5 text-sm text-gray-700 dark:text-gray-600'>{renderTimestamp(group, false)}</span>
</>
}
<div className='flex justify-between'>
<div className='flex items-center gap-1'>
<span className='truncate'><NotificationGroupDescription group={group} /></span>
{group.actors.length < 2 &&
<>
<span className='mt-px text-[8px] text-gray-700 dark:text-gray-600'>&bull;</span>
<span className='mt-0.5 text-sm text-gray-700 dark:text-gray-600'>{renderTimestamp(group, false)}</span>
</>
}
</div>
{/* Follow button for singular follow, reply, and mention */}
{group.actors.length === 1 && (group.type === 'follow' || group.type === 'reply' || group.type === 'mention') && !group.actors[0].followedByMe && (
<FollowButton
following={false}
followsYou={group.actors[0].followsMe}
handle={group.actors[0].handle}
variant="link"
/>
)}
</div>
}
</div>
Expand Down
Loading