feat: 联调点赞功能

This commit is contained in:
liuyonghe0111 2025-12-24 11:43:34 +08:00
parent 3ed5f603c4
commit e6b28751bd
5 changed files with 32 additions and 30 deletions

View File

@ -5,7 +5,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { IconButton } from '@/components/ui/button'; import { IconButton } from '@/components/ui/button';
import Link from 'next/link'; import Link from 'next/link';
import { getTranslations } from 'next-intl/server'; import { getTranslations } from 'next-intl/server';
import LikedIcon from '@/components/features/LikedIcon'; import LikedButton from '@/components/features/LikeButton';
import { LikeTargetType } from '@/services/editor/type'; import { LikeTargetType } from '@/services/editor/type';
export default async function Page({ params }: { params: Promise<{ id: string }> }) { export default async function Page({ params }: { params: Promise<{ id: string }> }) {
@ -20,11 +20,7 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
<Link href="/home"> <Link href="/home">
<IconButton variant="ghost" size="large" iconfont="icon-arrow-left" /> <IconButton variant="ghost" size="large" iconfont="icon-arrow-left" />
</Link> </Link>
<LikedIcon <LikedButton size="large" objectId={id} objectType={LikeTargetType.Character} />
iconProps={{ size: 'large' }}
objectId={id}
objectType={LikeTargetType.Character}
/>
</div> </div>
{/* 内容区 */} {/* 内容区 */}
<div className="mx-auto flex-1 overflow-auto pb-4 w-full"> <div className="mx-auto flex-1 overflow-auto pb-4 w-full">

View File

@ -23,7 +23,7 @@ import IconFont from '@/components/ui/iconFont';
import MaskCreate from './MaskCreate'; import MaskCreate from './MaskCreate';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
import LikedIcon from '@/components/features/LikedIcon'; import LikedButton from '@/components/features/LikeButton';
import { LikeTargetType } from '@/services/editor/type'; import { LikeTargetType } from '@/services/editor/type';
type SettingProps = { type SettingProps = {
@ -71,7 +71,7 @@ export default function SettingDialog({ open, onOpenChange }: SettingProps) {
<AlertDialogContent className="max-w-[500px]" showCloseButton={activeTab === 'profile'}> <AlertDialogContent className="max-w-[500px]" showCloseButton={activeTab === 'profile'}>
<AlertDialogTitle className="flex justify-between"> <AlertDialogTitle className="flex justify-between">
{activeTab === 'profile' ? ( {activeTab === 'profile' ? (
<LikedIcon objectId={characterId} objectType={LikeTargetType.Character} /> <LikedButton objectId={characterId} objectType={LikeTargetType.Character} />
) : ( ) : (
titleMap[activeTab] titleMap[activeTab]
)} )}

View File

@ -4,29 +4,31 @@ import { LikeTargetType, LikeType } from '@/services/editor/type';
import { IconButton } from '../ui/button'; import { IconButton } from '../ui/button';
import { useThmubObject } from '@/hooks/services/common'; import { useThmubObject } from '@/hooks/services/common';
import React from 'react'; import React from 'react';
import { cn } from '@/lib/utils';
type LikedIconProps = { type LikedIconProps = {
objectId: string; objectId: string;
objectType: LikeTargetType; objectType: LikeTargetType;
iconProps?: React.ComponentProps<typeof IconButton>; } & React.ComponentProps<typeof IconButton>;
};
const LikedIcon = React.memo((props: LikedIconProps) => { const LikedButton = React.memo((props: LikedIconProps) => {
const { objectId, objectType, iconProps } = props; const { objectId, objectType, ...iconProps } = props;
const { thumb, handleThumb, loading } = useThmubObject({ objectId, objectType }); const { thumb, handleThumb, loading } = useThmubObject({ objectId, objectType });
const isLiked = thumb === LikeType.Liked;
return ( return (
<IconButton <IconButton
variant="tertiary" variant="tertiary"
size="small" size="small"
{...iconProps} {...iconProps}
iconfont={thumb === LikeType.Liked ? 'icon-Like-fill' : 'icon-Like'} iconfont={isLiked ? 'icon-Like-fill' : 'icon-Like'}
className={cn(iconProps?.className, isLiked && 'text-red-500')}
onClick={() => { onClick={() => {
if (loading) return; if (loading) return;
handleThumb(thumb === LikeType.Liked ? LikeType.Canceled : LikeType.Liked); handleThumb(isLiked ? LikeType.Canceled : LikeType.Liked);
}} }}
/> />
); );
}); });
export default LikedIcon; export default LikedButton;

View File

@ -1,30 +1,35 @@
import { getLikeStatus, thmubObject } from '@/services/editor'; import { getLikeStatus, thmubObject } from '@/services/editor';
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { authKeys } from '@/lib/query-keys'; import { authKeys } from '@/lib/query-keys';
import { LikeObjectParamsType, LikeType } from '@/services/editor/type'; import { LikeObjectParamsType, LikeType } from '@/services/editor/type';
import { useAsyncFn } from '../tools'; import { useAsyncFn } from '../tools';
import { useCurrentUser } from '../auth';
export function useThmubObject(props: Pick<LikeObjectParamsType, 'objectId' | 'objectType'>) { export function useThmubObject(props: Pick<LikeObjectParamsType, 'objectId' | 'objectType'>) {
const queryClient = useQueryClient();
const [thumb, setThumb] = useState<LikeType>(); const [thumb, setThumb] = useState<LikeType>();
const user = queryClient.getQueryData(authKeys.currentUser()) as any; const { data: user } = useCurrentUser();
useQuery({ const { data: likeStatus, refetch: refetchLikeStatus } = useQuery({
queryKey: ['likeStatus', props.objectId, user.userId], queryKey: ['likeStatus', props.objectId, user?.userId],
enabled: !!props.objectId && !!user.userId, enabled: !!props.objectId && !!user?.userId,
queryFn: () => { queryFn: async () => {
return getLikeStatus({ objectId: props.objectId, userId: user.userId }); const { data } = await getLikeStatus({ objectId: props.objectId, userId: user?.userId });
return data.likeStatus;
}, },
}); });
useEffect(() => {
if (typeof likeStatus === 'number') {
setThumb(likeStatus);
}
}, [likeStatus]);
const { run: handleThumb, loading } = useAsyncFn(async (likeType: LikeType) => { const { run: handleThumb, loading } = useAsyncFn(async (likeType: LikeType) => {
setThumb(likeType); setThumb(likeType);
const user = queryClient.getQueryData(authKeys.currentUser()) as any; const { code } = await thmubObject({ ...props, likeType, userId: user?.userId });
const { data } = await thmubObject({ ...props, likeType, userId: user?.userId }); if (code === 200) {
if (data.code === 200) { refetchLikeStatus();
} else {
setThumb(undefined);
} }
}); });

View File

@ -23,6 +23,5 @@ export async function thmubObject(params: LikeObjectParamsType) {
} }
export async function getLikeStatus(params: Pick<LikeObjectParamsType, 'objectId' | 'userId'>) { export async function getLikeStatus(params: Pick<LikeObjectParamsType, 'objectId' | 'userId'>) {
const { data } = await editorRequest('/api/like/getLikeStatus', { method: 'POST', data: params }); return editorRequest('/api/like/getLikeStatus', { method: 'POST', data: params });
return data;
} }