11import React , { useContext , useState , useEffect } from 'react'
2- import { FaDev } from 'react-icons/fa' ;
32import devtoApi from '../services/devto'
4- import CardComponent from " ../components/CardComponent"
5- import ListComponent from " ../components/ListComponent"
6- import { format } from 'timeago.js' ;
3+ import CardComponent from ' ../components/CardComponent'
4+ import ListComponent from ' ../components/ListComponent'
5+ import { format } from 'timeago.js'
76import PreferencesContext from '../preferences/PreferencesContext'
8- import CardLink from " ../components/CardLink"
9- import { BiCommentDetail } from 'react-icons/bi' ;
10- import { MdAccessTime } from " react-icons/md"
11- import { AiOutlineLike } from " react-icons/ai"
7+ import CardLink from ' ../components/CardLink'
8+ import { BiCommentDetail } from 'react-icons/bi'
9+ import { MdAccessTime } from ' react-icons/md'
10+ import { AiOutlineLike } from ' react-icons/ai'
1211import CardItemWithActions from '../components/CardItemWithActions'
13- import ColoredLanguagesBadge from "../components/ColoredLanguagesBadge"
12+ import ColoredLanguagesBadge from '../components/ColoredLanguagesBadge'
13+ import SelectableCard from '../components/SelectableCard'
14+ import { GLOBAL_TAG , MY_LANGUAGES_TAG , MAX_MERGED_ITEMS_PER_LANGUAGE } from '../Constants'
15+ import { mergeMultipleDataSources } from '../utils/DataUtils'
16+ import { trackCardLanguageChange } from '../utils/Analytics'
1417
18+ const DT_MENU_LANGUAGE_ID = 'DT_MENU_LANGUAGE_ID'
1519
1620const ArticleItem = ( { item, index, analyticsTag } ) => {
17-
1821 const { listingMode } = useContext ( PreferencesContext )
1922
2023 return (
@@ -23,81 +26,134 @@ const ArticleItem = ({ item, index, analyticsTag }) => {
2326 index = { index }
2427 key = { index }
2528 item = { item }
26- cardItem = { (
29+ cardItem = {
2730 < >
2831 < CardLink link = { item . url } analyticsSource = { analyticsTag } >
29- { listingMode === "compact" &&
30- < div className = "counterWrapper" >
31- < AiOutlineLike />
32- < span className = "value" > { item . public_reactions_count } </ span >
33- </ div >
34- }
35- < div className = "subTitle" >
36- { item . title }
37- </ div >
32+ { listingMode === 'compact' && (
33+ < div className = "counterWrapper" >
34+ < AiOutlineLike />
35+ < span className = "value" > { item . public_reactions_count } </ span >
36+ </ div >
37+ ) }
38+ < div className = "subTitle" > { item . title } </ div >
3839 </ CardLink >
3940
40- {
41- listingMode === "normal" &&
41+ { listingMode === 'normal' && (
4242 < >
4343 < p className = "rowDescription" >
44- < span className = "rowItem" > < MdAccessTime className = { "rowTitleIcon" } /> { format ( new Date ( item . published_at ) ) } </ span >
45- < span className = "rowItem" > < BiCommentDetail className = { "rowTitleIcon" } /> { item . comments_count } comments</ span >
46- < span className = "rowItem" > < AiOutlineLike className = { "rowTitleIcon" } /> { item . public_reactions_count } reactions</ span >
44+ < span className = "rowItem" >
45+ < MdAccessTime className = { 'rowTitleIcon' } />
46+ { format ( new Date ( item . published_at ) ) }
47+ </ span >
48+ < span className = "rowItem" >
49+ < BiCommentDetail className = { 'rowTitleIcon' } />
50+ { item . comments_count } comments
51+ </ span >
52+ < span className = "rowItem" >
53+ < AiOutlineLike className = { 'rowTitleIcon' } />
54+ { item . public_reactions_count } reactions
55+ </ span >
4756 </ p >
4857 < p className = "rowDetails" >
4958 < ColoredLanguagesBadge languages = { item . tag_list } />
5059 </ p >
5160 </ >
52- }
53-
61+ ) }
5462 </ >
55- ) }
63+ }
5664 />
5765 )
5866}
5967
60-
61-
6268function DevToCard ( { analyticsTag, label, icon, withAds } ) {
6369 const preferences = useContext ( PreferencesContext )
64- const { userSelectedTags } = preferences
65-
70+ const { userSelectedTags, cardsSettings, dispatcher } = preferences
6671 const [ refresh , setRefresh ] = useState ( true )
72+ const [ selectedLanguage , setSelectedLanguage ] = useState ( )
73+ const [ cacheCardData , setCacheCardData ] = useState ( { } )
6774
6875 useEffect ( ( ) => {
69- setRefresh ( ! refresh )
70- } , [ userSelectedTags ] )
76+ if ( selectedLanguage ) {
77+ trackCardLanguageChange ( 'Devto' , selectedLanguage . value )
78+ dispatcher ( {
79+ type : 'setCardSettings' ,
80+ value : { card : label . toLowerCase ( ) , language : selectedLanguage . label } ,
81+ } )
82+ setRefresh ( ! refresh )
83+ }
84+ } , [ selectedLanguage ] )
7185
7286 const fetchArticles = async ( ) => {
73- const promises = userSelectedTags . map ( ( tag ) => {
74- if ( tag . devtoValues ) {
75- return devtoApi . getArticles ( tag . devtoValues [ 0 ] )
76- }
87+ if ( ! selectedLanguage ) {
7788 return [ ]
78- } )
79-
80- const results = await Promise . allSettled ( promises )
81- return results
82- . map ( ( res ) => {
83- let value = res . value
84- if ( res . status === 'rejected' ) {
85- value = [ ]
89+ }
90+
91+ if ( ! selectedLanguage . devtoValues ) {
92+ throw Error ( `Devto does not support ${ selectedLanguage ?. label } .` )
93+ }
94+
95+ let data = [ ]
96+ const cacheKey = `${ selectedLanguage . label } `
97+
98+ // Cache found
99+ if ( cacheCardData [ cacheKey ] ) {
100+ return cacheCardData [ cacheKey ]
101+ }
102+
103+ if ( selectedLanguage . value == MY_LANGUAGES_TAG . value ) {
104+ const selectedTagsArticlesPromises = userSelectedTags . map ( ( tag ) => {
105+ if ( tag . devtoValues ) {
106+ if ( cacheCardData [ tag . label ] ) {
107+ return cacheCardData [ tag . label ]
108+ } else {
109+ return devtoApi . getArticlesByCount ( tag . devtoValues [ 0 ] , MAX_MERGED_ITEMS_PER_LANGUAGE )
110+ }
86111 }
87- return value
112+ return [ ]
88113 } )
89- . flat ( )
90- . sort ( ( a , b ) => b . public_reactions_count - a . public_reactions_count )
114+
115+ data = await mergeMultipleDataSources (
116+ selectedTagsArticlesPromises ,
117+ MAX_MERGED_ITEMS_PER_LANGUAGE
118+ )
119+ } else {
120+ data = await devtoApi . getArticles ( selectedLanguage . devtoValues [ 0 ] )
121+ }
122+
123+ data = data . sort ( ( a , b ) => b . public_reactions_count - a . public_reactions_count )
124+
125+ setCacheCardData ( { ...cacheCardData , [ cacheKey ] : data } )
126+ return data
91127 }
92128
93129 const renderItem = ( item , index ) => (
94130 < ArticleItem item = { item } key = { `at-${ index } ` } index = { index } analyticsTag = { analyticsTag } />
95131 )
96132
133+ function HeaderTitle ( ) {
134+ return (
135+ < div style = { { display : 'inline-block' , margin : 0 , padding : 0 } } >
136+ < span > DevTo </ span >
137+ < SelectableCard
138+ isLanguage = { true }
139+ tagId = { DT_MENU_LANGUAGE_ID }
140+ selectedTag = { selectedLanguage }
141+ setSelectedTag = { setSelectedLanguage }
142+ fallbackTag = { GLOBAL_TAG }
143+ cardSettings = { cardsSettings ?. devto ?. language }
144+ data = { userSelectedTags . map ( ( tag ) => ( {
145+ label : tag . label ,
146+ value : tag . value ,
147+ } ) ) }
148+ />
149+ </ div >
150+ )
151+ }
152+
97153 return (
98154 < CardComponent
99155 icon = { < span className = "blockHeaderIcon" > { icon } </ span > }
100- title = { label }
156+ title = { < HeaderTitle /> }
101157 link = "https://dev.to/" >
102158 < ListComponent
103159 fetchData = { fetchArticles }
@@ -109,4 +165,4 @@ function DevToCard({ analyticsTag, label, icon, withAds }) {
109165 )
110166}
111167
112- export default DevToCard
168+ export default DevToCard
0 commit comments