码桶

发现社区成员的开源项目

奶狗 /

ng-webot

公开
main
ng-webot/web/src/components/RssConfig.tsx
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>
  )
}