fix(subscription): improve button spacing and eliminate toggle flicker

- Add visual separator between StarButton and SubscribeButton
- Use optimistic updates in useToggleSubscription for instant feedback
- Remove isLoading guard that caused button to unmount during refetch
This commit is contained in:
dongmucat 2026-04-29 14:47:17 +08:00
parent 7f47f8a702
commit a2adec2b06
3 changed files with 18 additions and 3 deletions

View file

@ -12,7 +12,7 @@ interface SubscribeButtonProps {
export function SubscribeButton({ skillId, subscriptionCount, onRequireLogin }: SubscribeButtonProps) {
const { t } = useTranslation()
const { data: subscriptionStatus, isLoading } = useSubscription(skillId)
const { data: subscriptionStatus } = useSubscription(skillId)
const toggleMutation = useToggleSubscription(skillId)
const { isAuthenticated } = useAuth()
@ -26,7 +26,7 @@ export function SubscribeButton({ skillId, subscriptionCount, onRequireLogin }:
}
}
if (isLoading || !subscriptionStatus) {
if (!subscriptionStatus) {
return null
}

View file

@ -44,7 +44,21 @@ export function useToggleSubscription(skillId: number) {
return useMutation({
mutationFn: (subscribed: boolean) => toggleSubscription(skillId, subscribed),
onSuccess: () => {
onMutate: async (currentSubscribed: boolean) => {
await queryClient.cancelQueries({ queryKey: ['skills', skillId, 'subscription'] })
const previous = queryClient.getQueryData<SubscriptionStatus>(['skills', skillId, 'subscription'])
queryClient.setQueryData<SubscriptionStatus>(
['skills', skillId, 'subscription'],
{ subscribed: !currentSubscribed },
)
return { previous }
},
onError: (_error, _variables, context) => {
if (context?.previous !== undefined) {
queryClient.setQueryData(['skills', skillId, 'subscription'], context.previous)
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['skills', skillId, 'subscription'] })
queryClient.invalidateQueries({ queryKey: ['skills'] })
queryClient.invalidateQueries({ queryKey: ['skills', 'subscriptions'] })

View file

@ -1070,6 +1070,7 @@ export function SkillDetailPage() {
{!isFetchingSkill ? (
<>
<StarButton skillId={skill.id} starCount={skill.starCount} onRequireLogin={requireLogin} />
<div className="h-px bg-border/40" />
<SubscribeButton skillId={skill.id} subscriptionCount={(skill as { subscriptionCount?: number }).subscriptionCount ?? 0} onRequireLogin={requireLogin} />
<RatingInput skillId={skill.id} onRequireLogin={requireLogin} />
</>