@@ -19,13 +19,22 @@ type FeedItem = {
1919const DEFAULT_FEEDS : Omit < Feed , "id" | "addedAt" > [ ] = [
2020 { url : "https://engineering.atspotify.com/feed" , title : "Spotify" , enabled : true } ,
2121 { url : "https://medium.com/feed/better-programming" , title : "Better Programming" , enabled : true } ,
22- { url : "https://blog.cloudflare.com/rss/" , title : "Cloudflare Blog" , enabled : true } ,
2322 { url : "https://overreacted.io/rss.xml" , title : "Overreacted" , enabled : true } ,
24- { url : "https://rss.beehiiv.com/feeds/ypr2bi0H9m.xml" , title : "Hungry Minds" , enabled : true } ,
23+ { url : "https://rss.beehiiv.com/feeds/ypr2bi0H9m.xml" , title : "Hungry Minds" , enabled : false } ,
24+ { url : "https://css-tricks.com/feed/" , title : "CSS Tricks" , enabled : true } ,
25+ { url : "https://feeds2.feedburner.com/tympanus" , title : "Codrops" , enabled : true } ,
26+ { url : "https://github.blog/feed/" , title : "GitHub" , enabled : true } ,
27+ { url : "https://blog.codinghorror.com/rss/" , title : "Coding Horror" , enabled : true } ,
28+ { url : "https://martinfowler.com/feed.atom" , title : "Martin Fowler" , enabled : true } ,
29+ {
30+ url : "https://www.tbray.org/ongoing/ongoing.atom" ,
31+ title : "ongoing by Tim Bray" ,
32+ enabled : false ,
33+ } ,
2534 {
2635 url : "https://www.thecrazyprogrammer.com/category/programming/feed" ,
2736 title : "The Crazy Programmer" ,
28- enabled : true ,
37+ enabled : false ,
2938 } ,
3039] ;
3140
@@ -39,7 +48,12 @@ function toRelativeTime(dateStr: string): string {
3948 const hours = Math . floor ( minutes / 60 ) ;
4049 if ( hours < 24 ) return `${ hours } h ago` ;
4150 const days = Math . floor ( hours / 24 ) ;
42- return `${ days } d ago` ;
51+
52+ if ( days < 30 ) return `${ days } d ago` ;
53+ const months = Math . floor ( days / 30 ) ;
54+ if ( months < 12 ) return `${ months } mo ago` ;
55+ const years = Math . floor ( months / 12 ) ;
56+ return `${ years } y ago` ;
4357}
4458
4559export function useRssFeeds ( ) {
@@ -196,9 +210,20 @@ export function useRssFeeds() {
196210 try {
197211 const enabledFeeds = feeds . filter ( f => f . enabled ) ;
198212 const results = await Promise . all ( enabledFeeds . map ( f => parseRss ( f . url , force ) ) ) ;
199- const flat = results
200- . flat ( )
201- . sort ( ( a , b ) => new Date ( b . published ) . getTime ( ) - new Date ( a . published ) . getTime ( ) ) ;
213+ // mixmatch same source entries, so that no entry with same source appears twice
214+ const mixed = results . flat ( ) . reduce (
215+ ( acc , item ) => {
216+ const key = `${ item . source } -${ item . title } -${ item . link } ` ;
217+ if ( ! acc [ key ] ) {
218+ acc [ key ] = item ;
219+ }
220+ return acc ;
221+ } ,
222+ { } as Record < string , FeedItem >
223+ ) ;
224+ const flat = Object . values ( mixed ) . sort (
225+ ( a , b ) => new Date ( b . published ) . getTime ( ) - new Date ( a . published ) . getTime ( )
226+ ) ;
202227 setItems ( flat ) ;
203228 } finally {
204229 setLoading ( false ) ;
0 commit comments