style(dxped): one Watch button for both feeds

The news pane had clickable callsign chips while an announcement had a
Watch button — two gestures for one act. The chips become plain labels
(they still show which calls are already watched) and the news card gets
the announcement's own Watch button and Open link.
This commit is contained in:
2026-08-31 18:42:39 +02:00
parent 187ac9aa84
commit 5b894b9dbc
2 changed files with 29 additions and 13 deletions
+27 -11
View File
@@ -178,7 +178,10 @@ export function DXpeditionsPanel() {
{news.length === 0 && !busy && (
<p className="text-xs text-muted-foreground text-center py-6">{t('dxp.noNews')}</p>
)}
{news.map((n, i) => (
{news.map((n, i) => {
const newsCalls = n.calls ?? [];
const newsAllWatched = newsCalls.length > 0 && newsCalls.every((c) => watched.has(c.toUpperCase()));
return (
<article key={`${n.link}-${i}`} className="rounded-md border border-border bg-muted/20 p-2">
<div className="flex items-start gap-2">
{n.image_url && (
@@ -194,6 +197,9 @@ export function DXpeditionsPanel() {
className="text-xs font-medium text-left hover:underline">{n.title}</button>
</div>
<p className="mt-0.5 text-[11px] text-muted-foreground line-clamp-3">{n.excerpt}</p>
{/* The callsigns are shown, not clicked: watching is the same
one button as an announcement, so the gesture is learned
once for the whole tab. */}
<div className="mt-1 flex items-center gap-1.5 flex-wrap">
{n.pub_date && (
<span className="text-[10px] text-muted-foreground">
@@ -201,22 +207,32 @@ export function DXpeditionsPanel() {
</span>
)}
{(n.calls ?? []).map((c) => (
<button key={c} type="button"
disabled={watched.has(c.toUpperCase())}
onClick={() => addAll([c])}
title={watched.has(c.toUpperCase()) ? t('dxp.watched') : t('dxp.watchTip')}
className={cn('px-1 py-px rounded font-mono text-[10px] border',
watched.has(c.toUpperCase())
? 'border-warning/50 text-warning'
: 'border-border text-info hover:bg-muted')}>
<span key={c} className={cn('font-mono text-[10px]',
watched.has(c.toUpperCase()) ? 'text-warning' : 'text-info')}>
{c}
</button>
</span>
))}
</div>
<div className="mt-1.5 flex items-center gap-2">
<Button variant={newsAllWatched ? 'ghost' : 'outline'} size="sm" className="h-6 text-[11px]"
disabled={newsCalls.length === 0 || newsAllWatched}
onClick={() => addAll(newsCalls)}
title={t('dxp.watchTip')}>
<Star className={cn('size-3', newsAllWatched && 'fill-current text-warning')} />
{newsAllWatched ? t('dxp.watched') : t('dxp.watch')}
</Button>
{n.link && (
<button type="button" onClick={() => BrowserOpenURL(n.link)}
className="text-[11px] text-muted-foreground hover:text-foreground inline-flex items-center gap-1">
<ExternalLink className="size-3" /> {t('dxp.open')}
</button>
)}
</div>
</div>
</div>
</article>
))}
);
})}
</div>
</section>
</div>