import React, { useCallback, useEffect, useState } from 'react';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Switch from '@mui/material/Switch';
import FormControlLabel from '@mui/material/FormControlLabel';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
import IconButton from '@mui/material/IconButton';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import RssFeedIcon from '@mui/icons-material/RssFeed';
import { getSettings, saveSettings } from '../../api/settings.js';
import { listCategories, updateCategoryProfile } from '../../api/forum.js';
import { showSnack } from '../snack.jsx';
/** 完整订阅 URL(运行时拼 origin,不硬编码域名) */
function feedUrl(path) {
return window.location.origin + path;
}
/** 版块图标小方块:icon 非 http 渲染 emoji/首字 */
function CatIcon({ c }) {
const isImg = /^https?:\/\//i.test(c.icon || '');
const letter = isImg ? (c.name || '?').charAt(0) : (c.icon || (c.name || '?').charAt(0));
return (
{letter}
);
}
/**
* RSS 订阅管理(admin,路由 /admin/rss):
* 1) 源管理:博客(常开只读)/ 论坛全站(feed_forum_enabled)/ 各版块(feed_enabled,即时保存)
* 2) 内容设置:feed_show_full(全文/摘要)+ feed_max_items(1-100)→ saveSettings
*/
export default function RssManage() {
const [cats, setCats] = useState([]);
const [forumFeed, setForumFeed] = useState(true);
const [showFull, setShowFull] = useState('0');
const [maxItems, setMaxItems] = useState('20');
const [saving, setSaving] = useState(false);
const load = useCallback(() => {
getSettings()
.then((s) => {
setForumFeed(s.feed_forum_enabled !== '0'); // 默认开启
setShowFull(s.feed_show_full === '1' ? '1' : '0');
setMaxItems(s.feed_max_items || '20');
})
.catch((e) => showSnack(e.message, 'error'));
listCategories()
.then((cs) => setCats(cs || []))
.catch((e) => showSnack(e.message, 'error'));
}, []);
useEffect(() => { load(); }, [load]);
const copyUrl = async (url) => {
try {
await navigator.clipboard.writeText(url);
showSnack('链接已复制');
} catch {
showSnack('复制失败,请手动复制', 'error');
}
};
// 论坛全站开关:即时保存
const toggleForumFeed = async (next) => {
setForumFeed(next);
try {
await saveSettings({ feed_forum_enabled: next ? '1' : '0' });
showSnack(next ? '已开启论坛订阅' : '已关闭论坛订阅');
} catch (e) {
setForumFeed(!next);
showSnack(e.message, 'error');
}
};
// 版块级开关:即时保存(feed_enabled 已进 profile 白名单)
const toggleCatFeed = async (cat, next) => {
const prev = cat.feed_enabled !== 0 && cat.feed_enabled !== '0';
setCats((cs) => cs.map((c) => (c.id === cat.id ? { ...c, feed_enabled: next ? 1 : 0 } : c)));
try {
await updateCategoryProfile(cat.id, { feed_enabled: next ? '1' : '0' });
showSnack(next ? `已开启「${cat.name}」订阅` : `已关闭「${cat.name}」订阅`);
} catch (e) {
setCats((cs) => cs.map((c) => (c.id === cat.id ? { ...c, feed_enabled: prev ? 1 : 0 } : c)));
showSnack(e.message, 'error');
}
};
const saveContent = async () => {
const n = parseInt(maxItems, 10);
if (isNaN(n) || n < 1 || n > 100) { showSnack('条目数需为 1-100 的数字', 'error'); return; }
setSaving(true);
try {
await saveSettings({ feed_show_full: showFull, feed_max_items: String(n) });
showSnack('内容设置已保存');
} catch (e) {
showSnack(e.message, 'error');
}
setSaving(false);
};
/** 源行:名称/描述 + URL(复制)+ 开关 */
const SourceRow = ({ name, desc, path, onToggle, checked, disabled, badge }) => (
{name}
{badge}
{desc && {desc}}
{feedUrl(path)}
copyUrl(feedUrl(path))} title="复制链接" sx={{ flexShrink: 0 }}>
{disabled ? (
) : (
onToggle(e.target.checked)} />}
label=""
sx={{ m: 0, flexShrink: 0 }}
/>
)}
);
return (
RSS 订阅
管理各位置的订阅源:订阅器(如 Feedly / Inoreader)可关注这些链接获取最新内容
{/* 源管理 */}
{/* 内容设置 */}
);
}