Skip to content

Commit 376bf27

Browse files
gfortaineGuillaume FORTAINE
and
Guillaume FORTAINE
authored
feat: Add pagination and more button at the bottom (#58)
Co-authored-by: Guillaume FORTAINE <[email protected]>
1 parent d2f76fa commit 376bf27

16 files changed

+228
-194
lines changed

app/item/[id]/page.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { notFound } from 'next/navigation';
2+
import Item from '../../../components/item';
3+
4+
// Utils
5+
import fetchData from '../../../lib/fetch-data';
6+
import { transform } from '../../../lib/get-item';
7+
8+
export default async function ItemPage({ params }) {
9+
const { id } = params;
10+
11+
if (!id) {
12+
notFound();
13+
}
14+
15+
const data = await fetchData(`item/${id}`);
16+
const story = transform(data);
17+
18+
return <Item story={story} />;
19+
}

app/item/page.js

Lines changed: 0 additions & 19 deletions
This file was deleted.

app/news/[page]/page.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Server Components
2+
import SystemInfo from '../../../components/server-info'
3+
4+
// Client Components
5+
import Stories from '../../../components/stories'
6+
import Footer from '../../../components/footer'
7+
8+
// Utils
9+
import fetchData from '../../../lib/fetch-data'
10+
11+
export default async function RSCPage({ params }) {
12+
const { page } = params;
13+
const storyIds = await fetchData('topstories')
14+
15+
return (
16+
<>
17+
<Stories page={page} storyIds={storyIds} />
18+
<Footer />
19+
<SystemInfo />
20+
</>
21+
)
22+
}

app/page.js

Lines changed: 0 additions & 30 deletions
This file was deleted.

components/header.module.css

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
}
1111

1212
.left {
13-
flex: 9;
13+
display: grid;
14+
grid-auto-flow: column;
15+
align-items: center;
1416
}
1517

1618
.right {
@@ -31,8 +33,7 @@
3133
a.login {
3234
padding: 10px;
3335
display: inline-block;
34-
font-size: 11px;
35-
text-transform: uppercase;
36+
font-size: 10pt;
3637
text-decoration: none;
3738
color: #000;
3839
}
@@ -47,6 +48,10 @@ a.login {
4748
}
4849

4950
@media (max-width: 750px) {
51+
.left {
52+
grid-auto-flow: row;
53+
}
54+
5055
.site-title {
5156
font-size: 16px;
5257
padding-bottom: 0;
@@ -58,5 +63,6 @@ a.login {
5863

5964
.nav {
6065
display: block;
66+
margin-left: 10px;
6167
}
6268
}

components/nav.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@ import styles from './nav.module.css'
33
export default function Nav() {
44
return (
55
<ul className={styles['nav-ul']}>
6-
<Item href="/">new</Item>
7-
<Item href="/">show</Item>
8-
<Item href="/">ask</Item>
9-
<Item href="/">jobs</Item>
10-
<Item href="/">submit</Item>
6+
<Item href="/newest">new</Item>
7+
{" | "}
8+
<Item href="/front">past</Item>
9+
{" | "}
10+
<Item href="/newcomments">show</Item>
11+
{" | "}
12+
<Item href="/ask">ask</Item>
13+
{" | "}
14+
<Item href="/show">show</Item>
15+
{" | "}
16+
<Item href="/jobs">jobs</Item>
17+
{" | "}
18+
<Item href="/submit">submit</Item>
1119
</ul>
1220
)
1321
}

components/nav.module.css

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88

99
.nav-ul li a {
1010
display: inline-block;
11-
padding: 10px;
12-
font-size: 11px;
13-
text-transform: uppercase;
11+
font-size: 10pt;
1412
text-decoration: none;
1513
color: #000;
1614
}

components/skeletons.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.item-skeleton {
22
margin: 5px 0;
33
overflow: hidden;
4+
flex: 100 1;
45
}
56

67
.item-skeleton:before,

components/stories.js

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
1-
import Story from './story'
21
import Link from 'next/link'
2+
import Story from './story'
3+
4+
import fetchData from '../lib/fetch-data'
5+
import { transform } from '../lib/get-item'
36

47
import styles from './stories.module.css'
58

6-
export default ({ stories, page = 1, offset = null }) => (
7-
<div>
8-
{stories.map((story, i) => (
9-
<div key={story.id} className={styles.item}>
10-
{null != offset ? (
11-
<span className={styles.count}>{i + offset + 1}</span>
12-
) : null}
13-
<div className={styles.story}>
14-
<Story {...story} />
9+
async function StoryWithData({ id }) {
10+
const data = await fetchData(`item/${id}`)
11+
const story = transform(data)
12+
13+
return <Story {...story} />
14+
}
15+
16+
export default async function Stories({ storyIds, page = 1 }) {
17+
const limit = 30
18+
const offset = (page - 1) * limit
19+
20+
return (
21+
<div>
22+
{storyIds.slice(offset, offset + limit).map((id, i) => (
23+
<div key={id} className={styles.item}>
24+
{null != offset ? (
25+
<span className={styles.count}>{i + offset + 1}</span>
26+
) : null}
27+
<StoryWithData id={id} key={id} />
1528
</div>
29+
))}
30+
<div className={styles.footer}>
31+
<Link href={`/news/${+page + 1}`}>More</Link>
1632
</div>
17-
))}
18-
<footer className={styles.footer}>
19-
<Link href={`/news?p=${page + 1}`}>More</Link>
20-
</footer>
21-
</div>
22-
)
33+
</div>
34+
)
35+
}

components/stories.module.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
.count {
77
flex-basis: auto;
88
flex-grow: 1;
9-
vertical-align: top;
109
font-size: 14px;
1110
padding-right: 5px;
1211
display: block;
@@ -24,7 +23,7 @@
2423
}
2524

2625
.footer {
27-
padding: 10px 0 40px 30px;
26+
padding: 10px 0 0 32px;
2827
}
2928

3029
.footer a {

components/story.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
'use client';
1+
'use client'
22

3-
import { useState } from 'react';
3+
import { useState } from 'react'
44

5-
import timeAgo from '../lib/time-ago';
5+
import timeAgo from '../lib/time-ago'
66

7-
import styles from './story.module.css';
7+
import styles from './story.module.css'
88

99
export default function Story({
1010
id,
@@ -13,45 +13,42 @@ export default function Story({
1313
url,
1414
user,
1515
score,
16-
commentsCount,
16+
commentsCount
1717
}) {
18-
const { host } = url ? new URL(url) : { host: '#' };
19-
const [voted, setVoted] = useState(false);
18+
const { host } = url ? new URL(url) : { host: '#' }
19+
const [voted, setVoted] = useState(false)
2020

2121
return (
22-
<div style={{ margin: '5px 0' }}>
22+
<div className={styles.story}>
2323
<div className={styles.title}>
2424
<span
25-
style={{
26-
cursor: 'pointer',
27-
fontFamily: 'sans-serif',
28-
marginRight: 5,
29-
color: voted ? '#ffa52a' : '#ccc',
30-
}}
25+
className={voted ? styles['votearrow--voted'] : styles.votearrow}
3126
onClick={() => setVoted(!voted)}
3227
>
3328
&#9650;
3429
</span>
3530
<a href={url}>{title}</a>
3631
{url && (
3732
<span className={styles.source}>
33+
{' ('}
3834
<a href={`http://${host}`}>{host.replace(/^www\./, '')}</a>
35+
{')'}
3936
</span>
4037
)}
4138
</div>
4239
<div className={styles.meta}>
4340
{score} {plural(score, 'point')} by{' '}
44-
<a href={`/user?id=${user}`}>{user}</a>{' '}
45-
<a href={`/item?id=${id}`}>
41+
<a href={`/user/${user}`}>{user}</a>{' '}
42+
<a href={`/item/${id}`}>
4643
{timeAgo(new Date(date)) /* note: we re-hydrate due to ssr */} ago
4744
</a>{' '}
4845
|{' '}
49-
<a href={`/item?id=${id}`}>
46+
<a href={`/item/${id}`}>
5047
{commentsCount} {plural(commentsCount, 'comment')}
5148
</a>
5249
</div>
5350
</div>
54-
);
51+
)
5552
}
5653

57-
const plural = (n, s) => s + (n === 0 || n > 1 ? 's' : '');
54+
const plural = (n, s) => s + (n === 0 || n > 1 ? 's' : '')

components/story.module.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
1+
.story {
2+
flex: 100 1;
3+
display: inline-block;
4+
}
5+
16
.title {
27
font-size: 15px;
38
margin-bottom: 3px;
49
}
510

11+
.votearrow {
12+
cursor: pointer;
13+
font-family: sans-serif;
14+
color: #ccc;
15+
}
16+
17+
.votearrow--voted {
18+
composes: votearrow;
19+
color: #ffa52a
20+
}
21+
622
.title > a {
723
color: #000;
824
text-decoration: none;

lib/get-item.js

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
1-
import fetchData from './fetch-data';
1+
import fetchData from './fetch-data'
22

33
export default async function (id) {
4-
const val = await fetchData(`item/${id}`);
4+
const val = await fetchData(`item/${id}`)
55
if (val) {
6-
return transform(val);
6+
return transform(val)
77
} else {
8-
return null;
8+
return null
99
}
1010
}
1111

1212
export function transform(val) {
13-
return {
14-
id: val.id,
15-
url: val.url || '',
16-
user: val.by,
17-
// time is seconds since epoch, not ms
18-
date: new Date(val.time * 1000).getTime() || 0,
19-
// sometimes `kids` is `undefined`
20-
comments: val.kids || [],
21-
commentsCount: val.descendants || 0,
22-
score: val.score,
23-
title: val.title,
24-
};
13+
if (val) {
14+
return {
15+
id: val.id,
16+
url: val.url || '',
17+
user: val.by,
18+
// time is seconds since epoch, not ms
19+
date: new Date(val.time * 1000).getTime() || 0,
20+
// sometimes `kids` is `undefined`
21+
comments: val.kids || [],
22+
commentsCount: val.descendants || 0,
23+
score: val.score,
24+
title: val.title
25+
}
26+
} else {
27+
return null
28+
}
2529
}

0 commit comments

Comments
 (0)