Skip to content

Commit 73168a0

Browse files
committed
feat: implement CardHeader component and refactor card components to utilize it for improved tag handling
1 parent 63f933e commit 73168a0

10 files changed

Lines changed: 171 additions & 159 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { MY_LANGUAGES_OPTION } from '../config'
2+
3+
type HeaderTitleProps = {
4+
label: string
5+
fallbackTag: {
6+
label: string
7+
value: string
8+
}
9+
selectedTag: {
10+
label: string
11+
value: string
12+
}
13+
children?: React.ReactNode
14+
}
15+
export const CardHeader = ({ label, fallbackTag, selectedTag, children }: HeaderTitleProps) => {
16+
if (children) {
17+
return <>{children}</>
18+
}
19+
20+
if (!selectedTag || selectedTag.value === fallbackTag.value) {
21+
return <>{label}</>
22+
}
23+
24+
if (selectedTag.value === MY_LANGUAGES_OPTION.value) {
25+
return (
26+
<>
27+
{label} <span className="blockHeaderHighlight">{MY_LANGUAGES_OPTION.label}</span>
28+
</>
29+
)
30+
}
31+
32+
return (
33+
<>
34+
{label}
35+
<span className="blockHeaderHighlight">{selectedTag.label}</span>
36+
</>
37+
)
38+
}

src/features/cards/components/CardSettings.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { LiaSortSolid } from 'react-icons/lia'
88
import { MdDateRange } from 'react-icons/md'
99
import { ref } from 'src/config'
1010
import { useUserPreferences } from 'src/stores/preferences'
11+
import { MY_LANGUAGES_OPTION } from '../config'
1112

1213
type SortOption = { label: string; value: string; icon?: React.ReactNode }
1314

@@ -24,7 +25,6 @@ type CardSettingsProps = {
2425
}
2526

2627
const DEFAULT_SORT_OPTIONS = [{ label: 'Newest', value: 'published_at', icon: <MdDateRange /> }]
27-
2828
export const CardSettings = ({
2929
id,
3030
url,
@@ -44,10 +44,13 @@ export const CardSettings = ({
4444

4545
const userTagsMemo = useMemo(() => {
4646
const newTags = userSelectedTags.sort((a, b) => a.label.localeCompare(b.label))
47+
let tags = [...newTags]
4748
if (globalTag) {
48-
return [globalTag, ...newTags]
49+
tags = [globalTag, ...tags]
4950
}
50-
return newTags
51+
52+
tags = [MY_LANGUAGES_OPTION, ...tags]
53+
return tags
5154
}, [userSelectedTags, globalTag])
5255

5356
const resolvedSortOptions =
@@ -78,17 +81,20 @@ export const CardSettings = ({
7881
</span>
7982
}>
8083
{userTagsMemo.map((tag) => (
81-
<MenuItem
82-
className={`menuItem`}
83-
type="radio"
84-
value={tag.value}
85-
key={tag.value}
86-
disabled={language === tag.value}
87-
onClick={() => {
88-
setCardSettings(id, { ...cardSettings, language: tag.value })
89-
}}>
90-
{tag.label}
91-
</MenuItem>
84+
<>
85+
<MenuItem
86+
className={`menuItem`}
87+
type="radio"
88+
value={tag.value}
89+
key={tag.value}
90+
disabled={language === tag.value}
91+
onClick={() => {
92+
setCardSettings(id, { ...cardSettings, language: tag.value })
93+
}}>
94+
{tag.label}
95+
</MenuItem>
96+
{tag.label.toLowerCase() === 'global' && <MenuDivider />}
97+
</>
9298
))}
9399
</SubMenu>
94100
)}

src/features/cards/components/devtoCard/DevtoCard.tsx

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,42 @@
1-
import { useMemo } from 'react'
21
import { AiOutlineLike } from 'react-icons/ai'
32
import { BiCommentDetail } from 'react-icons/bi'
43
import { Card, FloatingFilter } from 'src/components/Elements'
54
import { ListPostComponent } from 'src/components/List/ListPostComponent'
6-
import { useUserPreferences } from 'src/stores/preferences'
75
import { Article, CardPropsType } from 'src/types'
86
import { useGetSourceArticles } from '../../api/getSourceArticles'
7+
import { useSelectedTags } from '../../hooks/useSelectedTags'
8+
import { CardHeader } from '../CardHeader'
99
import { CardSettings } from '../CardSettings'
1010
import ArticleItem from './ArticleItem'
1111

1212
const GLOBAL_TAG = { label: 'Global', value: 'programming' }
1313

1414
export function DevtoCard(props: CardPropsType) {
1515
const { meta } = props
16-
const { userSelectedTags } = useUserPreferences()
17-
const cardSettings = useUserPreferences((state) => state.cardsSettings?.[meta.value])
1816

19-
const selectedTag = useMemo(() => {
20-
return userSelectedTags.find((lang) => lang.value === cardSettings?.language) || GLOBAL_TAG
21-
}, [userSelectedTags, cardSettings])
17+
const { queryTags, selectedTag, cardSettings } = useSelectedTags({
18+
source: meta.value,
19+
fallbackTag: GLOBAL_TAG,
20+
})
2221

2322
const {
2423
data: results,
2524
error,
2625
isLoading,
2726
} = useGetSourceArticles({
2827
source: 'devto',
29-
tags: [selectedTag.value],
28+
tags: queryTags.map((tag) => tag.value),
3029
})
3130

3231
const renderItem = (item: Article, index: number) => (
33-
<ArticleItem
34-
item={item}
35-
key={`at-${index}`}
36-
index={index}
37-
analyticsTag={meta.analyticsTag}
38-
selectedTag={selectedTag}
39-
/>
32+
<ArticleItem item={item} key={`at-${index}`} index={index} analyticsTag={meta.analyticsTag} />
4033
)
4134

42-
const HeaderTitle = () => {
43-
if (selectedTag.value === GLOBAL_TAG.value) {
44-
return <>{meta.label}</>
45-
}
46-
return (
47-
<>
48-
{meta.label}
49-
<span className="blockHeaderHighlight">{selectedTag.label}</span>
50-
</>
51-
)
52-
}
53-
5435
return (
5536
<Card
56-
titleComponent={<HeaderTitle />}
37+
titleComponent={
38+
<CardHeader label={meta.label} fallbackTag={GLOBAL_TAG} selectedTag={selectedTag} />
39+
}
5740
settingsComponent={
5841
<CardSettings
5942
url={meta.link}

src/features/cards/components/freecodecampCard/FreecodecampCard.tsx

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,35 @@
1-
import { useMemo } from 'react'
21
import { Card, FloatingFilter } from 'src/components/Elements'
32
import { ListPostComponent } from 'src/components/List/ListPostComponent'
4-
import { useUserPreferences } from 'src/stores/preferences'
53
import { Article, CardPropsType } from 'src/types'
64
import { useGetSourceArticles } from '../../api/getSourceArticles'
5+
import { useSelectedTags } from '../../hooks/useSelectedTags'
6+
import { CardHeader } from '../CardHeader'
77
import { CardSettings } from '../CardSettings'
88
import ArticleItem from './ArticleItem'
99

1010
const GLOBAL_TAG = { label: 'Global', value: 'programming' }
1111

1212
export function FreecodecampCard(props: CardPropsType) {
1313
const { meta } = props
14-
const { userSelectedTags } = useUserPreferences()
15-
const cardSettings = useUserPreferences((state) => state.cardsSettings?.[meta.value])
16-
17-
const selectedTag = useMemo(
18-
() => userSelectedTags.find((lang) => lang.value === cardSettings?.language) || GLOBAL_TAG,
19-
[userSelectedTags, cardSettings]
20-
)
14+
const { queryTags, selectedTag, cardSettings } = useSelectedTags({
15+
source: meta.value,
16+
fallbackTag: GLOBAL_TAG,
17+
})
2118

2219
const { data, isLoading } = useGetSourceArticles({
2320
source: 'freecodecamp',
24-
tags: [selectedTag.value],
21+
tags: queryTags.map((tag) => tag.value),
2522
})
2623

2724
const renderItem = (item: Article, index: number) => (
28-
<ArticleItem
29-
item={item}
30-
key={`fcc-${index}`}
31-
index={index}
32-
selectedTag={selectedTag}
33-
analyticsTag={meta.analyticsTag}
34-
/>
25+
<ArticleItem item={item} key={`fcc-${index}`} index={index} analyticsTag={meta.analyticsTag} />
3526
)
3627

37-
const HeaderTitle = () => {
38-
return (
39-
<>
40-
{meta.label}
41-
<span className="blockHeaderHighlight">{selectedTag.label}</span>
42-
</>
43-
)
44-
}
45-
4628
return (
4729
<Card
48-
titleComponent={<HeaderTitle />}
30+
titleComponent={
31+
<CardHeader label={meta.label} fallbackTag={GLOBAL_TAG} selectedTag={selectedTag} />
32+
}
4933
settingsComponent={
5034
<CardSettings
5135
url={meta.link}

src/features/cards/components/githubCard/GithubCard.tsx

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,30 @@ import { dateRanges } from 'src/config'
77
import { useUserPreferences } from 'src/stores/preferences'
88
import { CardPropsType, Repository } from 'src/types'
99
import { useGetGithubRepos } from '../../api/getGithubRepos'
10+
import { useSelectedTags } from '../../hooks/useSelectedTags'
11+
import { CardHeader } from '../CardHeader'
1012
import { CardSettings } from '../CardSettings'
1113
import RepoItem from './RepoItem'
1214

1315
const GLOBAL_TAG = { label: 'Global', value: 'global' }
1416

1517
export function GithubCard(props: CardPropsType) {
1618
const { meta } = props
17-
const userSelectedTags = useUserPreferences((state) => state.userSelectedTags)
18-
const setCardSettings = useUserPreferences((state) => state.setCardSettings)
19-
const cardSettings = useUserPreferences((state) => state.cardsSettings?.[meta.value])
2019

21-
const selectedTag = useMemo(
22-
() => userSelectedTags.find((lang) => lang.value === cardSettings?.language) || GLOBAL_TAG,
23-
[userSelectedTags, cardSettings]
24-
)
20+
const setCardSettings = useUserPreferences((state) => state.setCardSettings)
21+
const { queryTags, selectedTag, cardSettings } = useSelectedTags({
22+
source: meta.value,
23+
fallbackTag: GLOBAL_TAG,
24+
})
2525

2626
const selectedDateRange = useMemo(
2727
() => dateRanges.find((date) => date.value === cardSettings?.dateRange) || dateRanges[0],
2828
[cardSettings]
2929
)
3030

3131
const { data, error, isLoading } = useGetGithubRepos({
32-
tag: selectedTag.value,
32+
tags: queryTags.map((tag) => tag.value),
3333
dateRange: selectedDateRange.value,
34-
config: {
35-
enabled: !!selectedTag?.value,
36-
},
3734
})
3835

3936
const renderItem = (item: Repository, index: number) => (
@@ -46,19 +43,17 @@ export function GithubCard(props: CardPropsType) {
4643
/>
4744
)
4845

49-
const HeaderTitle = () => {
50-
return (
51-
<div>
52-
Github <span className="blockHeaderHighlight">{selectedTag.label}</span>{' '}
53-
<span className="blockHeaderHighlight">{selectedDateRange.label.toLowerCase()}</span>
54-
</div>
55-
)
56-
}
57-
5846
return (
5947
<Card
6048
fullBlock={true}
61-
titleComponent={<HeaderTitle />}
49+
titleComponent={
50+
<CardHeader label={meta.label} fallbackTag={GLOBAL_TAG} selectedTag={selectedTag}>
51+
<div>
52+
Github <span className="blockHeaderHighlight">{selectedTag.label}</span>{' '}
53+
<span className="blockHeaderHighlight">{selectedDateRange.label.toLowerCase()}</span>
54+
</div>
55+
</CardHeader>
56+
}
6257
settingsComponent={
6358
<CardSettings
6459
url={meta.link}

src/features/cards/components/hashnodeCard/HashnodeCard.tsx

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@ import { AiTwotoneHeart } from 'react-icons/ai'
22
import { BiCommentDetail } from 'react-icons/bi'
33
import { Card, FloatingFilter } from 'src/components/Elements'
44
import { ListPostComponent } from 'src/components/List/ListPostComponent'
5-
import { useUserPreferences } from 'src/stores/preferences'
65
import { Article, CardPropsType } from 'src/types'
76
import { useGetSourceArticles } from '../../api/getSourceArticles'
7+
import { useSelectedTags } from '../../hooks/useSelectedTags'
8+
import { CardHeader } from '../CardHeader'
89
import { CardSettings } from '../CardSettings'
910
import ArticleItem from './ArticleItem'
1011

1112
const GLOBAL_TAG = { label: 'Global', value: 'programming' }
1213

1314
export function HashnodeCard(props: CardPropsType) {
1415
const { meta } = props
15-
const cardSettings = useUserPreferences((state) => state.cardsSettings?.[meta.value])
16-
const { userSelectedTags } = useUserPreferences()
17-
const selectedTag =
18-
userSelectedTags.find((lang) => lang.value === cardSettings?.language) || GLOBAL_TAG
19-
16+
const { queryTags, selectedTag, cardSettings } = useSelectedTags({
17+
source: meta.value,
18+
fallbackTag: GLOBAL_TAG,
19+
})
2020
const { data, isLoading } = useGetSourceArticles({
2121
source: 'hashnode',
22-
tags: [selectedTag.value],
22+
tags: queryTags.map((tag) => tag.value),
2323
})
2424

2525
const renderItem = (item: Article, index: number) => (
@@ -32,20 +32,11 @@ export function HashnodeCard(props: CardPropsType) {
3232
/>
3333
)
3434

35-
const HeaderTitle = () => {
36-
return (
37-
<>
38-
{meta.label}
39-
{selectedTag.value != GLOBAL_TAG.value && (
40-
<span className="blockHeaderHighlight">{selectedTag.label}</span>
41-
)}
42-
</>
43-
)
44-
}
45-
4635
return (
4736
<Card
48-
titleComponent={<HeaderTitle />}
37+
titleComponent={
38+
<CardHeader label={meta.label} fallbackTag={GLOBAL_TAG} selectedTag={selectedTag} />
39+
}
4940
settingsComponent={
5041
<CardSettings
5142
url={meta.link}

0 commit comments

Comments
 (0)