Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated FollowButton with "unfollow" design #22057

Merged
merged 9 commits into from
Jan 30, 2025
Next Next commit
Updated FollowButton with "unfollow" design
ref https://linear.app/ghost/issue/AP-311/ui-to-unfollow-an-actor

- Added a confirmation modal on unfollow
- Made the button in "Following" state more subtle
- Made the label switch from "Following" to "Unfollow" on hover so it's clearer what it does
djordjevlais authored and allouis committed Jan 30, 2025
commit 25da664f1b03a2ca6b086a69a792d485ed138808
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ const ActivityItem: React.FC<ActivityItemProps> = ({children, url = null, onClic
onClick();
}
}}>
<div className='relative z-10 flex w-full gap-3 px-2 py-4'>
<div className='relative z-10 flex w-full items-center gap-3 px-2 py-4'>
{childrenArray[0]}
{childrenArray[1]}
{childrenArray[2]}
56 changes: 44 additions & 12 deletions apps/admin-x-activitypub/src/components/global/FollowButton.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,53 @@
import NiceModal from '@ebay/nice-modal-react';
import clsx from 'clsx';
import {Button, Modal} from '@tryghost/admin-x-design-system';
import {useEffect, useState} from 'react';

import {Button} from '@tryghost/admin-x-design-system';

import {useFollow} from '../../hooks/useActivityPubQueries';

interface FollowButtonProps {
className?: string;
following: boolean;
color?: 'black' | 'grey' | 'outline';
size?: 'sm' | 'md';
handle: string;
type?: 'button' | 'link';
onFollow?: () => void;
onUnfollow?: () => void;
}

const UnfollowModal = NiceModal.create(({onUnfollow}: {onUnfollow: () => void}) => {
const modal = NiceModal.useModal();

return (
<Modal
cancelLabel="Cancel"
okLabel="Unfollow"
size="sm"
onCancel={() => modal.remove()}
onOk={() => {
onUnfollow();
modal.remove();
}}
>
<p>Are you sure you want to unfollow this account?</p>
</Modal>
);
});

const noop = () => {};

const FollowButton: React.FC<FollowButtonProps> = ({
className,
color = 'black',
following,
handle,
size = 'md',
type = 'button',
onFollow = noop,
onUnfollow = noop
}) => {
const [isFollowing, setIsFollowing] = useState(following);
const [isHovered, setIsHovered] = useState(false);

const mutation = useFollow('index',
noop,
@@ -35,10 +59,13 @@ const FollowButton: React.FC<FollowButtonProps> = ({

const handleClick = async () => {
if (isFollowing) {
setIsFollowing(false);
onUnfollow();

// @TODO: Implement unfollow mutation
NiceModal.show(UnfollowModal, {
onUnfollow: () => {
setIsFollowing(false);
onUnfollow();
// @TODO: Implement unfollow mutation
}
});
} else {
setIsFollowing(true);
onFollow();
@@ -53,17 +80,22 @@ const FollowButton: React.FC<FollowButtonProps> = ({

return (
<Button
className={className}
color='black'
disabled={isFollowing}
label={isFollowing ? 'Following' : 'Follow'}
className={clsx(
className,
(type !== 'link') && 'min-w-[96px]',
(type === 'link' && isFollowing) && 'opacity-50'
)}
color={isFollowing ? 'outline' : color}
label={isFollowing ? (isHovered ? 'Unfollow' : 'Following') : 'Follow'}
link={type === 'link'}
size={size}
onClick={(event) => {
event?.preventDefault();
event?.stopPropagation();

handleClick();
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -96,8 +96,10 @@ const ActorList: React.FC<ActorListProps> = ({
</div>
<FollowButton
className='ml-auto'
color='grey'
following={isFollowing}
handle={getUsername(actor)}
size='sm'
type='link'
/>
</ActivityItem>
@@ -324,8 +326,10 @@ const ViewProfileModal: React.FC<ViewProfileModalProps> = ({
/>
</div>
<FollowButton
color='black'
following={profile.isFollowing}
handle={profile.handle}
size='md'
onFollow={onFollow}
onUnfollow={onUnfollow}
/>
@@ -347,9 +351,10 @@ const ViewProfileModal: React.FC<ViewProfileModalProps> = ({
<div className='absolute inset-x-0 bottom-0 h-16 bg-gradient-to-t from-white via-white/90 via-60% to-transparent' />
)}
{isOverflowing && <Button
className='absolute bottom-0 text-green'
className='absolute bottom-0'
label={isExpanded ? 'Show less' : 'Show all'}
link={true}
size='sm'
onClick={toggleExpand}
/>}
</div>)}