Skip to content

Commit 6a42b07

Browse files
committed
add Conferences card
1 parent a585f45 commit 6a42b07

7 files changed

Lines changed: 200 additions & 4 deletions

File tree

src/Constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react'
2-
import ConferencesCard from './cards/ConferencesCard'
32
import { SiGithub } from 'react-icons/si'
43
import { SiYcombinator } from 'react-icons/si'
54
import { FaDev } from 'react-icons/fa'
@@ -20,6 +19,8 @@ import { HashnodeCard } from 'src/features/hashnodeCard'
2019
import { MediumCard } from 'src/features/mediumCard'
2120
import { FreecodecampCard } from 'src/features/freecodecampCard'
2221
import { GithubCard } from 'src/features/githubCard'
22+
import { ConferencesCard } from 'src/features/conferencesCard'
23+
2324

2425

2526

src/components/List/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from 'react'
2-
import { ArticleType } from 'src/types'
2+
import { ArticleType, ConferenceType } from 'src/types'
33

44
export type ListComponentPropsType = {
5-
items: ArticleType[], // <== TODO: Unified Article Type
5+
items: ArticleType[] | ConferenceType[]
66
isLoading: boolean,
7-
renderItem: (item: ArticleType, index: number) => React.ReactNode,
7+
renderItem: (item: any, index: number) => React.ReactNode,
88
withAds: boolean,
99
placeholder?: React.ReactNode,
1010
refresh?: boolean,
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 { ConferenceType } from 'src/types'
4+
import { axios } from 'src/lib/axios'
5+
6+
const getConferences = async (tag: string): Promise<ConferenceType[]> => {
7+
return axios.get(`/data/v2/conferences/${tag}.json`)
8+
}
9+
10+
type QueryFnType = typeof getConferences
11+
12+
type UseGetConferencesOptions = {
13+
config?: QueryConfig<QueryFnType>
14+
tags: string[]
15+
}
16+
17+
export const useGetConferences = ({ config, tags }: UseGetConferencesOptions) => {
18+
return useQueries({
19+
queries: tags.map<UseQueryOptions<ConferenceType[]>>((tag) => {
20+
return {
21+
...config,
22+
queryKey: ['conferences', tag],
23+
queryFn: () => getConferences(tag),
24+
}
25+
})
26+
})
27+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import CardLink from 'src/components/CardLink'
2+
import CardItemWithActions from 'src/components/CardItemWithActions'
3+
import { Attributes } from 'src/lib/analytics'
4+
import { ConferenceItemPropsType } from 'src/types'
5+
import { MdAccessTime } from 'react-icons/md'
6+
import ColoredLanguagesBadge from 'src/components/ColoredLanguagesBadge'
7+
import { flag } from 'country-emoji'
8+
import { IoIosPin } from 'react-icons/io'
9+
import { RiCalendarEventFill } from 'react-icons/ri'
10+
11+
const ConferencesItem = (props: ConferenceItemPropsType) => {
12+
const { item, index, listingMode } = props
13+
14+
const ConferenceLocation = () => {
15+
if (item.online) {
16+
return '🌐 Online'
17+
}
18+
if (item.country) {
19+
return `${flag(item.country.replace(/[^a-zA-Z ]/g, ''))} ${item.city}`
20+
}
21+
}
22+
23+
const ConferenceDate = () => {
24+
if (!item.start_date) {
25+
return ''
26+
}
27+
const monthNames = [
28+
'January',
29+
'February',
30+
'March',
31+
'April',
32+
'May',
33+
'June',
34+
'July',
35+
'August',
36+
'September',
37+
'October',
38+
'November',
39+
'December',
40+
]
41+
const startDate = new Date(item.start_date)
42+
const startMnth = monthNames[startDate.getMonth()]
43+
let value = `${startMnth} ${('0' + startDate.getDate()).slice(-2)}`
44+
if (!item.end_date || item.end_date === item.start_date) {
45+
return value
46+
}
47+
const endDate = new Date(item.end_date)
48+
let endValue = ('0' + endDate.getDate()).slice(-2)
49+
if (endDate.getMonth() !== startDate.getMonth()) {
50+
endValue = `${monthNames[endDate.getMonth()]} ${endValue}`
51+
}
52+
return `${value} - ${endValue}`
53+
}
54+
return (
55+
<CardItemWithActions
56+
source={'conferences'}
57+
index={index}
58+
key={index}
59+
item={{ ...item, title: item.name }}
60+
cardItem={
61+
<>
62+
<CardLink
63+
link={item.url}
64+
analyticsAttributes={{
65+
[Attributes.TRIGERED_FROM]: 'card',
66+
[Attributes.TITLE]: item.name,
67+
[Attributes.LINK]: item.url,
68+
[Attributes.SOURCE]: 'conferences',
69+
}}>
70+
<RiCalendarEventFill className={'rowTitleIcon'} />
71+
{item.name}
72+
</CardLink>
73+
{listingMode === 'normal' ? (
74+
<>
75+
<div className="rowDescription">
76+
<span className="rowItem">
77+
<IoIosPin className="rowItemIcon" /> {ConferenceLocation()}
78+
</span>
79+
<span className="rowItem">
80+
<MdAccessTime className="rowItemIcon" /> {ConferenceDate()}
81+
</span>
82+
</div>
83+
<div className="rowDetails">
84+
<ColoredLanguagesBadge languages={[item.tag]} />
85+
</div>
86+
</>
87+
) : (
88+
<div className="rowDescription">
89+
<span className="rowItem">
90+
<MdAccessTime className="rowItemIcon" /> {ConferenceDate()}
91+
</span>
92+
</div>
93+
)}
94+
</>
95+
}
96+
/>
97+
)
98+
}
99+
100+
export default ConferencesItem
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import CardComponent from 'src/components/CardComponent'
2+
import { ListComponent } from 'src/components/List'
3+
import { useGetConferences } from '../api/getConferences'
4+
import { ConferenceType, CardPropsType } from 'src/types'
5+
import { useUserPreferences } from 'src/stores/preferences'
6+
import { getCardTagsValue } from 'src/utils/DataEnhancement'
7+
import ConferenceItem from './ConferenceItem'
8+
9+
10+
export function ConferencesCard(props: CardPropsType) {
11+
const { label, icon, withAds } = props
12+
const { userSelectedTags, listingMode } = useUserPreferences()
13+
14+
const results = useGetConferences({ tags: getCardTagsValue(userSelectedTags, 'devtoValues') })
15+
16+
const isLoading = results.some((result) => result.isLoading)
17+
18+
const getData = () => {
19+
return results
20+
.reduce((acc: ConferenceType[], curr) => {
21+
if (!curr.data) return acc
22+
return [...acc, ...curr.data]
23+
}, [])
24+
.sort((a, b) => a.start_date - b.start_date)
25+
}
26+
27+
const renderItem = (item: ConferenceType, index: number) => (
28+
<ConferenceItem
29+
item={item}
30+
key={`cf-${index}`}
31+
index={index}
32+
listingMode={listingMode}
33+
/>
34+
)
35+
36+
return (
37+
<CardComponent
38+
icon={<span className="blockHeaderIcon">{icon}</span>}
39+
link="https://confs.tech/"
40+
title={label}>
41+
<ListComponent
42+
items={getData()}
43+
isLoading={isLoading}
44+
renderItem={renderItem}
45+
withAds={withAds}
46+
/>
47+
</CardComponent>
48+
)
49+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./components/ConferencesCard";

src/types/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ export type ArticleType = {
5454
starsInDateRange?: string
5555
}
5656

57+
export type ConferenceType = {
58+
id: string
59+
name: string
60+
url: string
61+
start_date: number
62+
end_date: number
63+
tag: string
64+
online: Boolean
65+
city?: string
66+
country?: string
67+
}
68+
5769
export type CardPropsType = {
5870
label: string
5971
icon: React.ReactNode
@@ -72,3 +84,9 @@ export type CardSettingsType = {
7284
language: string
7385
dateRange?: string
7486
}
87+
88+
export type ConferenceItemPropsType = {
89+
index: number
90+
item: ConferenceType
91+
listingMode?: ListingMode
92+
}

0 commit comments

Comments
 (0)