Files
jiang13-forum/frontend/src/hooks/useForumLimits.ts

115 lines
3.3 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 { useEffect, useState } from 'react';
import { api } from '../api/client';
import type { ForumLimitsPublic } from '../api/types';
import { DEFAULT_ASIDE_WIDGETS, DEFAULT_FEED_SORT_TABS } from '../api/types';
const DEFAULT_LIMITS: ForumLimitsPublic = {
post_title_max: 128,
post_tags_max: 256,
post_content_max: 50000,
comment_max: 5000,
comment_edit_window_minutes: 3,
search_keyword_min: 1,
search_keyword_max: 50,
page_size_default: 30,
password_min_len: 6,
avatar_max_mb: 2,
signature_max: 200,
open_posts_in_new_tab: true,
open_content_links_in_new_tab: true,
aside_show_tag_cloud: false,
aside_show_recent_comments: false,
aside_show_friend_links: true,
aside_show_showcase: false,
aside_widgets: DEFAULT_ASIDE_WIDGETS,
nav_show_friend_links: true,
footer_show_friend_links: true,
nav_show_showcase: false,
footer_show_showcase: false,
feed_list_style: 'title',
feed_sort_tabs: DEFAULT_FEED_SORT_TABS,
permalink_enabled: false,
permalink_ext: 'html',
monitor_pageview: false,
};
let cached: ForumLimitsPublic | null = null;
let inflight: Promise<ForumLimitsPublic> | null = null;
let cacheEpoch = 0;
const listeners = new Set<() => void>();
function fetchLimits(): Promise<ForumLimitsPublic> {
if (cached) return Promise.resolve(cached);
if (inflight) return inflight;
inflight = api.forumLimits()
.then(limits => {
cached = limits;
return limits;
})
.catch(() => cached ?? DEFAULT_LIMITS)
.finally(() => { inflight = null; });
return inflight;
}
/** 获取前台可见的论坛限制配置 */
export function useForumLimits() {
const [limits, setLimits] = useState<ForumLimitsPublic>(cached ?? DEFAULT_LIMITS);
const [loading, setLoading] = useState(!cached);
const [epoch, setEpoch] = useState(cacheEpoch);
useEffect(() => {
const onInvalidate = () => setEpoch(cacheEpoch);
listeners.add(onInvalidate);
return () => { listeners.delete(onInvalidate); };
}, []);
useEffect(() => {
let cancelled = false;
// 无缓存时显示加载中,避免首页用默认 30/300 误拉全量
if (!cached) setLoading(true);
fetchLimits()
.then(next => {
if (!cancelled) setLimits(next);
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => { cancelled = true; };
}, [epoch]);
return { limits, loading };
}
/** 冷启动 / 预热:确保 limits 已写入模块缓存;force 时重拉并在成功后通知 hook */
export async function ensureForumLimitsLoaded(opts?: { force?: boolean }): Promise<ForumLimitsPublic> {
if (opts?.force) {
cached = null;
inflight = null;
}
const limits = await fetchLimits();
if (opts?.force) {
cacheEpoch += 1;
listeners.forEach(fn => fn());
}
return limits;
}
/** 文档 SSR / 管理端:同步写入 limits 模块缓存 */
export function seedForumLimitsCache(limits: ForumLimitsPublic) {
cached = limits;
cacheEpoch += 1;
listeners.forEach(fn => fn());
}
/** 清除缓存并通知已挂载的 hook 重新拉取 */
export function invalidateForumLimitsCache() {
cached = null;
cacheEpoch += 1;
listeners.forEach(fn => fn());
}
/** 同步读取已缓存的论坛限制(供路径生成等非 hook 场景) */
export function getCachedForumLimits(): ForumLimitsPublic {
return cached ?? DEFAULT_LIMITS;
}