|
1 | 1 | import { Trans } from '@lingui/macro' |
2 | | -import { EmptyStatus, Image, LoadingStatus } from '@masknet/shared' |
3 | | -import { EMPTY_OBJECT } from '@masknet/shared-base' |
4 | | -import { makeStyles } from '@masknet/theme' |
| 2 | +import { ElementAnchor, EmptyStatus, Image, LoadingStatus, ReloadStatus } from '@masknet/shared' |
| 3 | +import { EMPTY_LIST } from '@masknet/shared-base' |
| 4 | +import { LoadingBase, makeStyles } from '@masknet/theme' |
| 5 | +import type { ParsedEvent } from '@masknet/web3-providers/types' |
5 | 6 | import { Link, Typography } from '@mui/material' |
6 | 7 | import { format } from 'date-fns' |
7 | | -import { useCallback, useEffect, useMemo } from 'react' |
| 8 | +import { uniq } from 'lodash-es' |
| 9 | +import { useEffect, useMemo } from 'react' |
8 | 10 | import { useNewsList } from '../../hooks/useEventList.js' |
9 | 11 |
|
10 | 12 | const useStyles = makeStyles()((theme) => ({ |
@@ -102,83 +104,132 @@ const useStyles = makeStyles()((theme) => ({ |
102 | 104 | color: theme.palette.maskColor.main, |
103 | 105 | padding: '10px 0', |
104 | 106 | }, |
| 107 | + loading: { |
| 108 | + color: theme.palette.maskColor.main, |
| 109 | + }, |
105 | 110 | })) |
106 | 111 |
|
107 | 112 | interface NewsListProps { |
108 | 113 | date: Date |
109 | 114 | onDatesUpdate(/** locale date string list */ dates: string[]): void |
110 | 115 | } |
111 | 116 |
|
| 117 | +interface Group { |
| 118 | + date: number |
| 119 | + /** 2024/10/10 */ |
| 120 | + label: string |
| 121 | + events: ParsedEvent[] |
| 122 | +} |
112 | 123 | export function NewsList({ date, onDatesUpdate }: NewsListProps) { |
113 | | - const { data: list = EMPTY_OBJECT, isLoading } = useNewsList(date) |
114 | | - const dateString = date.toLocaleDateString() |
115 | | - const empty = !Object.keys(list).length |
116 | 124 | const { classes, cx } = useStyles() |
117 | | - const futureNewsList = useMemo(() => { |
118 | | - const newsList: string[] = [] |
119 | | - for (const key in list) { |
120 | | - if (new Date(key) >= date) { |
121 | | - newsList.push(key) |
| 125 | + const { isLoading, isFetching, data: newsList, error, hasNextPage, fetchNextPage } = useNewsList(date) |
| 126 | + |
| 127 | + const groups = useMemo(() => { |
| 128 | + if (!newsList?.length) return EMPTY_LIST |
| 129 | + const groups: Group[] = [] |
| 130 | + newsList.forEach((event) => { |
| 131 | + const eventDate = new Date(event.event_date) |
| 132 | + if (eventDate < date) return |
| 133 | + const label = eventDate.toLocaleDateString() |
| 134 | + const group: Group = groups.find((g) => g.label === label) || { |
| 135 | + date: event.event_date, |
| 136 | + label, |
| 137 | + events: [], |
122 | 138 | } |
123 | | - } |
124 | | - return newsList |
125 | | - }, [list, date]) |
| 139 | + if (!group.events.length) groups.push(group) |
| 140 | + group.events.push(event) |
| 141 | + }) |
| 142 | + return groups |
| 143 | + }, [newsList, date]) |
126 | 144 |
|
127 | 145 | useEffect(() => { |
128 | | - onDatesUpdate(Object.keys(list)) |
129 | | - }, [list, onDatesUpdate]) |
130 | | - |
131 | | - const listRef = useCallback((el: HTMLDivElement | null) => { |
132 | | - el?.scrollTo({ top: 0 }) |
133 | | - }, []) |
| 146 | + if (!newsList) return onDatesUpdate(EMPTY_LIST) |
| 147 | + onDatesUpdate(uniq(newsList.map((x) => new Date(x.event_date).toLocaleDateString()))) |
| 148 | + }, [onDatesUpdate, newsList]) |
134 | 149 |
|
135 | | - return ( |
136 | | - <div className={classes.container} ref={listRef} key={dateString}> |
137 | | - <div className={classes.paddingWrap}> |
138 | | - {isLoading && empty ? |
| 150 | + if (isLoading) { |
| 151 | + return ( |
| 152 | + <div className={classes.container}> |
| 153 | + <div className={classes.paddingWrap}> |
139 | 154 | <div className={cx(classes.empty, classes.eventTitle)}> |
140 | 155 | <LoadingStatus /> |
141 | 156 | </div> |
142 | | - : !empty && futureNewsList.length ? |
143 | | - futureNewsList.map((key) => { |
144 | | - return ( |
145 | | - <div key={key}> |
146 | | - <Typography className={classes.dateDiv}> |
147 | | - {format(new Date(key), 'MMM dd,yyy')} |
148 | | - </Typography> |
149 | | - {list[key].map((event) => ( |
150 | | - <Link |
151 | | - key={event.event_url} |
152 | | - href={event.event_url} |
153 | | - className={classes.eventCard} |
154 | | - rel="noopener noreferrer" |
155 | | - target="_blank"> |
156 | | - <div className={classes.eventHeader}> |
157 | | - <div className={classes.projectWrap}> |
158 | | - <Image |
159 | | - src={event.project?.logo || event.poster_url} |
160 | | - classes={{ container: classes.logo }} |
161 | | - size={24} |
162 | | - alt={event.project?.name || event.event_title} |
163 | | - /> |
164 | | - <Typography className={classes.projectName}> |
165 | | - {event.project?.name || event.event_title} |
166 | | - </Typography> |
167 | | - </div> |
168 | | - <Typography className={classes.eventType}>{event.event_type}</Typography> |
| 157 | + </div> |
| 158 | + </div> |
| 159 | + ) |
| 160 | + } |
| 161 | + |
| 162 | + if (error) { |
| 163 | + return ( |
| 164 | + <div className={classes.container}> |
| 165 | + <div className={classes.paddingWrap}> |
| 166 | + <div className={cx(classes.empty, classes.eventTitle)}> |
| 167 | + <ReloadStatus message={error.message}></ReloadStatus> |
| 168 | + </div> |
| 169 | + </div> |
| 170 | + </div> |
| 171 | + ) |
| 172 | + } |
| 173 | + if (!groups.length) { |
| 174 | + return ( |
| 175 | + <div className={classes.container}> |
| 176 | + <div className={classes.paddingWrap}> |
| 177 | + <div className={cx(classes.empty, classes.eventTitle)}> |
| 178 | + <EmptyStatus> |
| 179 | + <Trans>No content for the last two weeks.</Trans> |
| 180 | + </EmptyStatus> |
| 181 | + </div> |
| 182 | + </div> |
| 183 | + </div> |
| 184 | + ) |
| 185 | + } |
| 186 | + |
| 187 | + return ( |
| 188 | + <div className={classes.container}> |
| 189 | + <div className={classes.paddingWrap}> |
| 190 | + {groups.map((group) => { |
| 191 | + return ( |
| 192 | + <div key={group.label}> |
| 193 | + <Typography className={classes.dateDiv}> |
| 194 | + {format(new Date(group.date), 'MMM dd,yyy')} |
| 195 | + </Typography> |
| 196 | + {group.events.map((event) => ( |
| 197 | + <Link |
| 198 | + key={event.event_url} |
| 199 | + href={event.event_url} |
| 200 | + className={classes.eventCard} |
| 201 | + rel="noopener noreferrer" |
| 202 | + target="_blank"> |
| 203 | + <div className={classes.eventHeader}> |
| 204 | + <div className={classes.projectWrap}> |
| 205 | + <Image |
| 206 | + src={event.project?.logo || event.poster_url} |
| 207 | + classes={{ container: classes.logo }} |
| 208 | + size={24} |
| 209 | + alt={event.project?.name || event.event_title} |
| 210 | + /> |
| 211 | + <Typography className={classes.projectName}> |
| 212 | + {event.project?.name || event.event_title} |
| 213 | + </Typography> |
169 | 214 | </div> |
170 | | - <Typography className={classes.eventTitle}>{event.event_title}</Typography> |
171 | | - <Typography className={classes.eventContent}> |
172 | | - {event.event_description} |
173 | | - </Typography> |
174 | | - </Link> |
175 | | - ))} |
176 | | - </div> |
177 | | - ) |
178 | | - }) |
179 | | - : <EmptyStatus className={classes.empty}> |
180 | | - <Trans>No content for the last two weeks.</Trans> |
181 | | - </EmptyStatus> |
| 215 | + <Typography className={classes.eventType}>{event.event_type}</Typography> |
| 216 | + </div> |
| 217 | + <Typography className={classes.eventTitle}>{event.event_title}</Typography> |
| 218 | + <Typography className={classes.eventContent}>{event.event_description}</Typography> |
| 219 | + </Link> |
| 220 | + ))} |
| 221 | + </div> |
| 222 | + ) |
| 223 | + })} |
| 224 | + {hasNextPage ? |
| 225 | + <ElementAnchor height={30} callback={() => fetchNextPage()}> |
| 226 | + {isFetching ? |
| 227 | + <LoadingBase className={classes.loading} /> |
| 228 | + : null} |
| 229 | + </ElementAnchor> |
| 230 | + : <Typography color={(theme) => theme.palette.maskColor.second} textAlign="center" py={2}> |
| 231 | + <Trans>No more data available.</Trans> |
| 232 | + </Typography> |
182 | 233 | } |
183 | 234 | </div> |
184 | 235 | </div> |
|
0 commit comments