Files
JKVideo/hooks/useFollow.ts
Developer 6e0dc2729c 本项目已收到哔哩哔哩(bilibili)律师函,要求停止对 B 站 API 的调用及相关仿制行为。
为尊重知识产权及相关法律法规,本仓库即日起停止后续维护与更新,不再接受新的 Issue 和 Pull Request。

现有代码仅作学习参考保留,请勿将本项目用于任何商业或违法用途。

感谢所有支持过本项目的朋友。
2026-05-12 21:29:45 +08:00

69 lines
2.0 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.
import { useCallback, useEffect, useRef, useState } from "react";
import { getRelation, modifyRelation } from "../services/bilibili";
import { useAuthStore } from "../store/authStore";
import { getSecure } from "../utils/secureStorage";
import { toast } from "../utils/toast";
/**
* 关注 / 取消关注 UP 主。
* - 未登录toggle 时 toast 提示,按钮 disabled=false 仍允许点击
* - 已登录但无 bili_jcttoggle 时 toast 提示让用户重登
* - 正常路径:乐观更新 UI请求失败回滚
*/
export function useFollow(mid: number | undefined) {
const isLoggedIn = useAuthStore((s) => s.isLoggedIn);
const [following, setFollowing] = useState(false);
const [loading, setLoading] = useState(false);
const inflightRef = useRef(false);
// 首次拉取 / mid 切换时刷新关注状态
useEffect(() => {
if (!mid || !isLoggedIn) {
setFollowing(false);
return;
}
let cancelled = false;
getRelation(mid)
.then((r) => {
if (!cancelled) setFollowing(r.following);
})
.catch(() => {});
return () => {
cancelled = true;
};
}, [mid, isLoggedIn]);
const toggle = useCallback(async () => {
if (!mid) return;
if (!isLoggedIn) {
toast("请先登录后再关注");
return;
}
const biliJct = await getSecure("bili_jct");
if (!biliJct) {
toast("请重新登录后再使用关注功能");
return;
}
if (inflightRef.current) return;
inflightRef.current = true;
setLoading(true);
const next = !following;
setFollowing(next); // 乐观更新
try {
await modifyRelation(mid, next ? 1 : 2);
} catch (e: any) {
setFollowing(!next); // 失败回滚
if (e?.message === "NO_CSRF") {
toast("请重新登录后再使用关注功能");
} else {
toast(`操作失败:${e?.message || "未知错误"}`);
}
} finally {
inflightRef.current = false;
setLoading(false);
}
}, [mid, isLoggedIn, following]);
return { following, loading, toggle };
}