UI-UX/src/hooks/useVoteAction.ts

77 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, useCallback } from "react";
import { useRouter, usePathname } from "next/navigation";
import { useSession } from "next-auth/react";
import toast from "react-hot-toast";
import { useVoteStore } from "@/lib/store";
import type { Artist } from "@/types/artist";
interface UseVoteActionResult {
/** 当前投票目标艺人null 时弹窗关闭) */
target: Artist | null;
/** 触发投票(自动检查登录态) */
openVote: (artist: Artist) => void;
/** 关闭投票弹窗 */
closeVote: () => void;
/** 确认投票(已登录态下调用) */
confirmVote: (artist: Artist, count: number) => Promise<void>;
}
/**
* 投票交互统一入口。
*
* - 未登录 → 提示并跳登录页(登录后回到当前路径)
* - 已登录 → 打开投票弹窗 → 确认后调用本地 store + 尝试调用 API
* - 任意态 → 用 toast 反馈结果
*
* 注意:当前已取消所有投票数量限制(无每日上限 / 无单艺人上限)。
*/
export function useVoteAction(): UseVoteActionResult {
const router = useRouter();
const pathname = usePathname();
const { status } = useSession();
const recordVote = useVoteStore((s) => s.vote);
const [target, setTarget] = useState<Artist | null>(null);
const openVote = useCallback(
(artist: Artist) => {
if (status === "loading") return; // 会话还在加载,等一下
if (status === "unauthenticated") {
toast("请先登录后再为偶像投票", { icon: "🔐" });
const back = encodeURIComponent(pathname || "/");
setTimeout(() => router.push(`/login?callbackUrl=${back}`), 350);
return;
}
setTarget(artist);
},
[status, pathname, router],
);
const closeVote = useCallback(() => setTarget(null), []);
const confirmVote = useCallback(
async (artist: Artist, count: number) => {
// 1. 立即更新本地 store + 反馈UI 0 延迟)
recordVote(artist.id, count);
toast.success(`已为 ${artist.name} 投出 ${count}`);
setTarget(null);
// 2. 后台 fire-and-forget 调用真实 API5 秒超时,失败静默忽略)
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);
fetch("/api/vote", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ artistId: artist.id, count }),
signal: ctrl.signal,
})
.catch(() => {})
.finally(() => clearTimeout(timer));
},
[recordVote],
);
return { target, openVote, closeVote, confirmVote };
}