@@ -2,6 +2,15 @@ import { useEffect, useRef, useState } from 'preact/hooks';
22import type { FeedRecord } from '../api/contracts' ;
33import { DominantField } from './DominantField' ;
44
5+ interface JsonFeedItem {
6+ title ?: string ;
7+ content_text ?: string ;
8+ }
9+
10+ interface JsonFeedResponse {
11+ items ?: JsonFeedItem [ ] ;
12+ }
13+
514interface ResultDisplayProps {
615 result : FeedRecord ;
716 onCreateAnother : ( ) => void ;
@@ -27,25 +36,16 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
2736
2837 const loadPreview = async ( ) => {
2938 try {
30- const response = await window . fetch ( fullUrl ) ;
31- const xml = await response . text ( ) ;
32- const document = new DOMParser ( ) . parseFromString ( xml , 'application/xml' ) ;
33- const explicitTitles = Array . from ( document . querySelectorAll ( 'item > title, entry > title' ) ) . map (
34- ( node ) => node . textContent ?. trim ( )
35- ) ;
36- const derivedDescriptions = Array . from ( document . querySelectorAll ( 'item > description' ) )
37- . map ( ( node ) => node . textContent ?. trim ( ) )
38- . filter ( ( description ) : description is string => Boolean ( description ) )
39- . filter ( ( description ) => / ^ \d + \. \s + / . test ( description ) )
40- . map ( ( description ) =>
41- description
42- . replace ( / ^ \d + \. \s + / , '' )
43- . replace ( / \s + \( [ ^ ) ] * \) \s * $ / , '' )
44- . trim ( )
45- ) ;
46- const itemTitles = [ ...explicitTitles , ...derivedDescriptions ]
47- . filter ( ( title ) : title is string => Boolean ( title ) )
48- . slice ( 0 , 3 ) ;
39+ const response = await window . fetch ( fullUrl , {
40+ headers : { Accept : 'application/feed+json' } ,
41+ } ) ;
42+ if ( ! response . ok ) throw new Error ( 'Preview request failed' ) ;
43+ const payload = ( await response . json ( ) ) as JsonFeedResponse ;
44+ const itemTitles =
45+ payload . items
46+ ?. map ( ( item ) => normalizePreviewText ( item . title || item . content_text ) )
47+ . filter ( ( title ) : title is string => Boolean ( title ) )
48+ . slice ( 0 , 3 ) || [ ] ;
4949
5050 if ( ! isCancelled ) setPreviewItems ( itemTitles ) ;
5151 } catch {
@@ -84,9 +84,19 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
8484 readOnly
8585 actionLabel = "Copy feed URL"
8686 actionText = "Copy"
87+ actionVariant = "soft"
8788 onAction = { ( ) => void copyToClipboard ( fullUrl ) }
8889 />
8990
91+ < div class = "result-actions result-actions--quiet" >
92+ < a href = { fullUrl } class = "btn btn--ghost btn--linkish" target = "_blank" rel = "noopener noreferrer" >
93+ Open feed
94+ </ a >
95+ < button type = "button" class = "btn btn--quiet btn--linkish" onClick = { onCreateAnother } >
96+ Create another feed
97+ </ button >
98+ </ div >
99+
90100 { previewItems . length > 0 && (
91101 < section class = "result-preview" aria-label = "Feed preview" >
92102 < p class = "result-preview__label" > Latest items</ p >
@@ -98,15 +108,6 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
98108 </ section >
99109 ) }
100110
101- < div class = "result-actions result-actions--quiet" >
102- < a href = { fullUrl } class = "btn btn--ghost btn--linkish" target = "_blank" rel = "noopener noreferrer" >
103- Open feed
104- </ a >
105- < button type = "button" class = "btn btn--quiet btn--linkish" onClick = { onCreateAnother } >
106- Create another feed
107- </ button >
108- </ div >
109-
110111 { copyNotice && (
111112 < div class = "notice notice--success" role = "status" >
112113 < p > { copyNotice } </ p >
@@ -115,3 +116,15 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
115116 </ section >
116117 ) ;
117118}
119+
120+ function normalizePreviewText ( value ?: string ) : string | null {
121+ if ( ! value ) return null ;
122+
123+ const normalized = value
124+ . replace ( / \s + / g, ' ' )
125+ . replace ( / ^ \d + \. \s + / , '' )
126+ . replace ( / \s + \( [ ^ ) ] * \) \s * $ / , '' )
127+ . trim ( ) ;
128+
129+ return normalized || null ;
130+ }
0 commit comments