Files
JKVideo/hooks/useFollow.ts

69 lines
2.0 KiB
TypeScript
Raw Normal View History

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 };
}