Skip to content

Commit 99cff02

Browse files
committed
add freecodecamp card
1 parent f54fd4e commit 99cff02

5 files changed

Lines changed: 175 additions & 2 deletions

File tree

src/Constants.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react'
22
import ConferencesCard from './cards/ConferencesCard'
33
import ReposCard from './cards/ReposCard'
4-
import FreeCodeCampCard from './cards/FreeCodeCampCard'
54
import { SiGithub } from 'react-icons/si'
65
import { SiYcombinator } from 'react-icons/si'
76
import { FaDev } from 'react-icons/fa'
@@ -20,6 +19,8 @@ import { LobstersCard } from 'src/features/lobstersCard'
2019
import { DevtoCard } from 'src/features/devtoCard'
2120
import { HashnodeCard } from 'src/features/hashnodeCard'
2221
import { MediumCard } from 'src/features/mediumCard'
22+
import { FreecodecampCard } from 'src/features/freecodecampCard'
23+
2324

2425

2526

@@ -102,7 +103,7 @@ export const SUPPORTED_CARDS = [
102103
icon: <FaFreeCodeCamp className="blockHeaderWhite" />,
103104
analyticsTag: 'freecodecamp',
104105
label: 'FreeCodeCamp',
105-
component: FreeCodeCampCard,
106+
component: FreecodecampCard,
106107
},
107108
{
108109
value: 'indiehackers',
@@ -158,6 +159,7 @@ export const GLOBAL_TAG = {
158159
devtoValues: ['programming'],
159160
hashnodeValues: ['programming'],
160161
mediumValues: ['programming'],
162+
freecodecampValues: ['programming']
161163
}
162164
export const MY_LANGUAGES_TAG = {
163165
value: 'myLangs',
@@ -166,6 +168,7 @@ export const MY_LANGUAGES_TAG = {
166168
devtoValues: ['myLangs'],
167169
hashnodeValues: ['myLangs'],
168170
mediumValues: ['myLangs'],
171+
freecodecampValues: ['myLangs']
169172
}
170173
export const MAX_MERGED_ITEMS_PER_LANGUAGE = 10
171174
export { APP }
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useQueries, UseQueryOptions } from '@tanstack/react-query'
2+
import { QueryConfig } from 'src/lib/react-query'
3+
import { ArticleType } from 'src/types'
4+
import { axios } from 'src/lib/axios'
5+
6+
const getArticles = async (tag: string): Promise<ArticleType[]> => {
7+
return axios.get(`/data/v2/freecodecamp/${tag}.json`)
8+
}
9+
10+
type QueryFnType = typeof getArticles
11+
12+
type UseGetArticlesOptions = {
13+
config?: QueryConfig<QueryFnType>
14+
tags: string[]
15+
}
16+
17+
export const useGetArticles = ({ config, tags }: UseGetArticlesOptions) => {
18+
return useQueries({
19+
queries: tags.map<UseQueryOptions<ArticleType[]>>((tag) => {
20+
return {
21+
...config,
22+
queryKey: ['freecodecampArticles', tag],
23+
queryFn: () => getArticles(tag),
24+
}
25+
})
26+
})
27+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import CardLink from 'src/components/CardLink'
2+
import CardItemWithActions from 'src/components/CardItemWithActions'
3+
import { Attributes } from 'src/lib/analytics'
4+
import { ArticleItemPropsType } from 'src/types'
5+
import { format } from 'timeago.js'
6+
import { MdAccessTime } from 'react-icons/md'
7+
import ColoredLanguagesBadge from 'src/components/ColoredLanguagesBadge'
8+
9+
const ArticleItem = (props: ArticleItemPropsType) => {
10+
const { item, index, selectedTag } = props
11+
return (
12+
<CardItemWithActions
13+
source={'freecodecamp'}
14+
index={index}
15+
key={index}
16+
item={item}
17+
cardItem={
18+
<>
19+
<CardLink
20+
link={item.url}
21+
analyticsAttributes={{
22+
[Attributes.TRIGERED_FROM]: 'card',
23+
[Attributes.TITLE]: item.title,
24+
[Attributes.LINK]: item.url,
25+
[Attributes.SOURCE]: 'freecodecamp',
26+
[Attributes.LANGUAGE]: selectedTag?.value,
27+
}}>
28+
<div className="subTitle">{item.title}</div>
29+
</CardLink>
30+
<>
31+
<p className="rowDescription">
32+
<span className="rowItem">
33+
<MdAccessTime className={'rowTitleIcon'} />
34+
{format(new Date(item.published_at))}
35+
</span>
36+
</p>
37+
<p className="rowDetails">
38+
<ColoredLanguagesBadge languages={item.tags.slice(0, 4)} />
39+
</p>
40+
</>
41+
</>
42+
}
43+
/>
44+
)
45+
}
46+
47+
export default ArticleItem
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useState, useEffect } from 'react'
2+
import CardComponent from 'src/components/CardComponent'
3+
import { ListComponent } from 'src/components/List'
4+
import { useGetArticles } from '../api/getArticles'
5+
import { ArticleType, CardPropsType } from 'src/types'
6+
import { useUserPreferences } from 'src/stores/preferences'
7+
import { getCardTagsValue } from 'src/utils/DataEnhancement'
8+
import ArticleItem from './ArticleItem'
9+
import { Tag } from 'src/features/remoteConfig'
10+
import { GLOBAL_TAG, MY_LANGUAGES_TAG } from 'src/Constants'
11+
import { trackCardLanguageSelect } from 'src/lib/analytics'
12+
import SelectableCard from 'src/components/SelectableCard'
13+
14+
const FCC_MENU_LANGUAGE_ID = 'FCC_MENU_LANGUAGE_ID'
15+
16+
export function FreecodecampCard(props: CardPropsType) {
17+
const { label, icon, withAds } = props
18+
const { userSelectedTags, cardsSettings, setCardSettings, listingMode } = useUserPreferences()
19+
const [selectedTag, setSelectedTag] = useState<Tag>()
20+
21+
useEffect(() => {
22+
if (selectedTag) {
23+
setCardSettings(label.toLowerCase(), { language: selectedTag.label })
24+
}
25+
}, [selectedTag])
26+
27+
const getQueryTags = () => {
28+
if (!selectedTag) {
29+
return []
30+
}
31+
32+
if (selectedTag.value === MY_LANGUAGES_TAG.freecodecampValues[0]) {
33+
return getCardTagsValue(userSelectedTags, 'freecodecampValues')
34+
}
35+
return selectedTag.freecodecampValues
36+
}
37+
38+
const results = useGetArticles({ tags: getQueryTags() })
39+
40+
const getIsLoading = () => results.some((result) => result.isLoading)
41+
42+
const getData = () => {
43+
return results
44+
.reduce((acc: ArticleType[], curr) => {
45+
if (!curr.data) return acc
46+
return [...acc, ...curr.data]
47+
}, [])
48+
.sort((a, b) => b.reactions - a.reactions)
49+
}
50+
51+
const renderItem = (item: ArticleType, index: number) => (
52+
<ArticleItem
53+
item={item}
54+
key={`fcc-${index}`}
55+
index={index}
56+
selectedTag={selectedTag}
57+
listingMode={listingMode}
58+
/>
59+
)
60+
61+
const HeaderTitle = () => {
62+
return (
63+
<div style={{ display: 'inline-block', margin: 0, padding: 0 }}>
64+
<span> {label} </span>
65+
<SelectableCard
66+
isLanguage={true}
67+
tagId={FCC_MENU_LANGUAGE_ID}
68+
selectedTag={selectedTag}
69+
setSelectedTag={setSelectedTag}
70+
fallbackTag={GLOBAL_TAG}
71+
cardSettings={cardsSettings?.devto?.language}
72+
trackEvent={(tag: Tag) => trackCardLanguageSelect('Devto', tag.value)}
73+
data={userSelectedTags.map((tag) => ({
74+
label: tag.label,
75+
value: tag.value,
76+
}))}
77+
/>
78+
</div>
79+
)
80+
}
81+
82+
return (
83+
<CardComponent
84+
icon={<span className="blockHeaderIcon">{icon}</span>}
85+
link="https://freecodecamp.com/news"
86+
title={<HeaderTitle />}>
87+
<ListComponent
88+
items={getData()}
89+
isLoading={getIsLoading()}
90+
renderItem={renderItem}
91+
withAds={withAds}
92+
/>
93+
</CardComponent>
94+
)
95+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./components/FreecodecampCard";

0 commit comments

Comments
 (0)