码桶
发现社区成员的开源项目
RssConfig.tsx3.9 KB
import { useEffect, useState, useCallback } from 'react'
import { api } from '@/lib/api'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Loader2, Plus, Trash2, Rss } from 'lucide-react'
import { toast } from 'sonner'
interface Feed {
id: number
title: string
feed_url: string
}
export function RssConfig({ botId }: { botId: number }) {
const [feeds, setFeeds] = useState<Feed[]>([])
const [loading, setLoading] = useState(false)
const [title, setTitle] = useState('')
const [url, setUrl] = useState('')
const [adding, setAdding] = useState(false)
const load = useCallback(async () => {
setLoading(true)
try {
const d = await api.post('/api/market/rss_list', { bot_id: botId })
if (d.ok) setFeeds(d.feeds || [])
} catch (e: any) {
toast.error(e.message)
} finally {
setLoading(false)
}
}, [botId])
useEffect(() => { load() }, [load])
async function add() {
const u = url.trim()
if (!u) { toast.error('请输入订阅源地址'); return }
setAdding(true)
try {
const d = await api.post('/api/market/rss_add', { bot_id: botId, title: title.trim(), feed_url: u })
if (!d.ok) throw new Error(d.msg)
toast.success('已添加订阅源')
setTitle('')
setUrl('')
load()
} catch (e: any) {
toast.error(e.message)
} finally {
setAdding(false)
}
}
async function del(id: number) {
try {
const d = await api.post('/api/market/rss_del', { bot_id: botId, id })
if (!d.ok) throw new Error(d.msg)
toast.success('已删除')
load()
} catch (e: any) {
toast.error(e.message)
}
}
return (
<div className="space-y-4">
<p className="text-[11px] leading-relaxed text-muted-foreground">
RSS 订阅推送:添加订阅源后,在微信中向机器人发送「最新」即可推送各订阅源最新 3 条文章(标题 + 链接)。
</p>
<div className="space-y-2">
<div className="flex items-center gap-1.5 text-xs font-medium">
<Rss className="h-3.5 w-3.5 text-muted-foreground" /> 订阅源({feeds.length})
</div>
{loading ? (
<div className="flex justify-center py-2"><Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /></div>
) : feeds.length === 0 ? (
<p className="text-[11px] text-muted-foreground">尚未添加任何订阅源。</p>
) : (
<div className="space-y-1.5">
{feeds.map(f => (
<div key={f.id} className="flex items-start gap-2 rounded border p-2">
<div className="flex-1 min-w-0 text-[11px]">
<div className="font-medium truncate">{f.title || '(未命名)'}</div>
<div className="text-muted-foreground truncate">{f.feed_url}</div>
</div>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0" onClick={() => del(f.id)} aria-label="删除"><Trash2 className="h-3.5 w-3.5 text-destructive" /></Button>
</div>
))}
</div>
)}
</div>
<div className="space-y-1.5 rounded-md border p-2.5">
<Label className="text-xs">添加订阅源</Label>
<Input value={title} placeholder="名称(可选),如:少数派" onChange={e => setTitle(e.target.value)} className="h-7 text-xs" />
<Input value={url} placeholder="订阅地址,如:https://sspai.com/feed" onChange={e => setUrl(e.target.value)} className="h-7 text-xs" />
<Button size="sm" className="h-7 w-full text-xs" disabled={adding} onClick={add}>
{adding ? <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" /> : <Plus className="mr-1 h-3.5 w-3.5" />}
添加订阅源
</Button>
</div>
</div>
)
}