import type { FeedTokenCreatedView, FeedTokenView } from '@dorfteich/shared'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FormError } from '../components/forms'; import { apiDelete, apiGet, apiPost } from '../lib/api'; const FEED_TOKENS_QUERY_KEY = ['users', 'me', 'feed-tokens'] as const; /** * Feed-token management in the user settings (issue #149): create, the * one-time secret reveal with a ready-to-paste feed URL, and the list with * delete. Feed tokens are read-only and only authenticate the Atom feeds. */ export function FeedTokensSection(): React.JSX.Element { const { t, i18n } = useTranslation('apiTokens'); const queryClient = useQueryClient(); const [name, setName] = useState(''); const [created, setCreated] = useState(null); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const tokens = useQuery({ queryKey: FEED_TOKENS_QUERY_KEY, queryFn: () => apiGet('/users/me/feed-tokens'), }); const invalidate = () => queryClient.invalidateQueries({ queryKey: FEED_TOKENS_QUERY_KEY }); const submit = async (event: React.FormEvent): Promise => { event.preventDefault(); setError(null); setCreated(null); setBusy(true); try { const view = await apiPost('/users/me/feed-tokens', { name }); setCreated(view); setName(''); await invalidate(); } catch (err) { setError(err); } finally { setBusy(false); } }; const remove = async (id: string): Promise => { await apiDelete(`/users/me/feed-tokens/${id}`); await invalidate(); }; const formatTime = (iso: string): string => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }).format(new Date(iso)); return (

{t('feed.title')}

{t('feed.intro')}

void submit(event)}> {created && (

{t('feed.revealHint')}

{created.token}

{t('feed.urlHint', { url: `${window.location.origin}/api/v1/public/<${t('feed.pondPlaceholder')}>/feed.xml?token=${created.token}`, })}

)} {tokens.data && tokens.data.length === 0 &&

{t('feed.empty')}

} {tokens.data && tokens.data.length > 0 && ( {tokens.data.map((token) => ( ))}
{t('fields.name')} {t('list.created')} {t('list.lastUsed')} {t('common:tableActions')}
{token.name} {formatTime(token.createdAt)} {token.lastUsedAt ? formatTime(token.lastUsedAt) : '—'}
)}
); }