Add experimental example of next-news (#9249)
* Add next-news example * Update components * Use getStaticProps * Move directory
This commit is contained in:
parent
6707909690
commit
4c6e294ca2
30 changed files with 884 additions and 0 deletions
2
examples/z-experimental-next-news/.gitignore
vendored
Normal file
2
examples/z-experimental-next-news/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
.next
|
23
examples/z-experimental-next-news/components/comment-form.js
Normal file
23
examples/z-experimental-next-news/components/comment-form.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
export default () => (
|
||||
<div>
|
||||
<textarea />
|
||||
<button>add comment</button>
|
||||
<style jsx>{`
|
||||
textarea {
|
||||
width: 400px;
|
||||
height: 100px;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 3px 4px;
|
||||
}
|
||||
@media (max-width: 750px) {
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
84
examples/z-experimental-next-news/components/comment.js
Normal file
84
examples/z-experimental-next-news/components/comment.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import timeAgo from '../lib/time-ago'
|
||||
import React from 'react'
|
||||
|
||||
export default class Comment extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
this.toggle = this.toggle.bind(this)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { user, text, date, comments } = this.props
|
||||
return (
|
||||
<div className='comment'>
|
||||
<div className='meta'>
|
||||
{user} {timeAgo(new Date(date))} ago{' '}
|
||||
<span onClick={this.toggle} className='toggle'>
|
||||
{this.state.toggled
|
||||
? `[+${(this.props.commentsCount || 0) + 1}]`
|
||||
: '[-]'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{this.state.toggled
|
||||
? null
|
||||
: [
|
||||
<div
|
||||
key='text'
|
||||
className='text'
|
||||
dangerouslySetInnerHTML={{ __html: text }}
|
||||
/>,
|
||||
<div key='children' className='children'>
|
||||
{comments.map(comment => (
|
||||
<Comment key={comment.id} {...comment} />
|
||||
))}
|
||||
</div>
|
||||
]}
|
||||
|
||||
<style jsx>{`
|
||||
.comment {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.children {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #000;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
/* hn styles */
|
||||
|
||||
.text :global(p) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.text :global(pre) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.text :global(a) {
|
||||
color: #000;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
toggle () {
|
||||
this.setState({ toggled: !this.state.toggled })
|
||||
}
|
||||
}
|
91
examples/z-experimental-next-news/components/header.js
Normal file
91
examples/z-experimental-next-news/components/header.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
import Nav from './nav'
|
||||
import Logo from './logo'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default () => (
|
||||
<header>
|
||||
<div className='left'>
|
||||
<Link href='/'>
|
||||
<a>
|
||||
<span className='logo'>
|
||||
<Logo />
|
||||
</span>
|
||||
<span className='title'>Hacker Next</span>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='nav'>
|
||||
<Nav />
|
||||
</div>
|
||||
</div>
|
||||
<div className='right'>
|
||||
<Link href='/login'>
|
||||
<a className='login'>login</a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
header {
|
||||
background: #ffa52a;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 4px 5px 2px 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.left {
|
||||
flex: 9;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
padding: 8px 10px 8px 4px;
|
||||
color: #000;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
a.login {
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.login:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
.title {
|
||||
font-size: 16px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
a.login {
|
||||
padding: 24px 10px 23px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</header>
|
||||
)
|
45
examples/z-experimental-next-news/components/item.js
Normal file
45
examples/z-experimental-next-news/components/item.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
import Story from '../components/story'
|
||||
import Comment from '../components/comment'
|
||||
import CommentForm from '../components/comment-form'
|
||||
|
||||
export default ({ story, comments = null }) => (
|
||||
<div className='item'>
|
||||
<Story {...story} />
|
||||
|
||||
<div className='form'>
|
||||
<CommentForm />
|
||||
</div>
|
||||
|
||||
<div className='comments'>
|
||||
{comments ? (
|
||||
comments.map(comment => <Comment key={comment.id} {...comment} />)
|
||||
) : (
|
||||
<div className='loading'>Loading…</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.item {
|
||||
padding: 10px 29px;
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.comments {
|
||||
padding: 10px 0 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
.item {
|
||||
padding: 8px 0px;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
27
examples/z-experimental-next-news/components/login-form.js
Normal file
27
examples/z-experimental-next-news/components/login-form.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
export default () => (
|
||||
<div>
|
||||
<h4>Login</h4>
|
||||
<p>
|
||||
username: <input type='text' />
|
||||
<br />
|
||||
password: <input type='password' />
|
||||
</p>
|
||||
<button>login</button>
|
||||
<p>
|
||||
<a href='#'>Forgot your password?</a>
|
||||
</p>
|
||||
<h4>Create Account</h4>
|
||||
<p>
|
||||
username: <input type='text' />
|
||||
<br />
|
||||
password: <input type='password' />
|
||||
</p>
|
||||
<button>create account</button>
|
||||
|
||||
<style jsx>{`
|
||||
p {
|
||||
line-height: 22px;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
15
examples/z-experimental-next-news/components/logo.js
Normal file
15
examples/z-experimental-next-news/components/logo.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
export default () => (
|
||||
<span>
|
||||
N
|
||||
<style jsx>{`
|
||||
span {
|
||||
border: 1px solid #fff;
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
`}</style>
|
||||
</span>
|
||||
)
|
56
examples/z-experimental-next-news/components/meta.js
Normal file
56
examples/z-experimental-next-news/components/meta.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
import Head from 'next/head'
|
||||
import NProgress from 'nprogress'
|
||||
import Router from 'next/router'
|
||||
|
||||
Router.onRouteChangeStart = () => NProgress.start()
|
||||
Router.onRouteChangeComplete = () => NProgress.done()
|
||||
Router.onRouteChangeError = () => NProgress.done()
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<meta charSet='utf-8' />
|
||||
</Head>
|
||||
<style jsx global>{`
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
||||
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* loading progress bar styles */
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: #ff9300;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
#nprogress .peg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px #ff9300, 0 0 5px #ff9300;
|
||||
opacity: 1;
|
||||
transform: rotate(3deg) translate(0px, -4px);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
44
examples/z-experimental-next-news/components/nav.js
Normal file
44
examples/z-experimental-next-news/components/nav.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
export default () => (
|
||||
<ul>
|
||||
<Item href='/newest'>new</Item>
|
||||
<Item href='/show'>show</Item>
|
||||
<Item href='/ask'>ask</Item>
|
||||
<Item href='/jobs'>jobs</Item>
|
||||
<Item href='/submit'>submit</Item>
|
||||
|
||||
<style jsx>{`
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
`}</style>
|
||||
</ul>
|
||||
)
|
||||
|
||||
const Item = ({ href, children }) => (
|
||||
<li>
|
||||
<Link href={href}>
|
||||
<a>{children}</a>
|
||||
</Link>
|
||||
|
||||
<style jsx>{`
|
||||
li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
`}</style>
|
||||
</li>
|
||||
)
|
32
examples/z-experimental-next-news/components/page.js
Normal file
32
examples/z-experimental-next-news/components/page.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import Header from './header'
|
||||
import Meta from './meta'
|
||||
|
||||
export default ({ children }) => (
|
||||
<div className='main'>
|
||||
<Meta />
|
||||
<Header />
|
||||
|
||||
<div className='page'>{children}</div>
|
||||
|
||||
<style jsx>{`
|
||||
.main {
|
||||
width: 85%;
|
||||
margin: auto;
|
||||
padding: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.page {
|
||||
color: #828282;
|
||||
background: #fff;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
.main {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
60
examples/z-experimental-next-news/components/stories.js
Normal file
60
examples/z-experimental-next-news/components/stories.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import Story from './updating-story'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default ({ stories, page = 1, offset = null }) => (
|
||||
<div>
|
||||
{stories.map((story, i) => (
|
||||
<div key={story.id} className='item'>
|
||||
{offset != null ? (
|
||||
<span className='count'>{i + offset + 1}</span>
|
||||
) : null}
|
||||
<div className='story'>
|
||||
<Story {...story} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<footer className='footer'>
|
||||
<Link href='/news/[id]' as={`/news/${page + 1}`}>
|
||||
<a>More</a>
|
||||
</Link>
|
||||
</footer>
|
||||
|
||||
<style jsx>{`
|
||||
.item {
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.count {
|
||||
flex-basis: auto;
|
||||
flex-grow: 1;
|
||||
vertical-align: top;
|
||||
font-size: 14px;
|
||||
padding-right: 5px;
|
||||
display: block;
|
||||
width: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.count::after {
|
||||
content: '.';
|
||||
}
|
||||
|
||||
.story {
|
||||
flex: 100;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 10px 0 40px 30px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
80
examples/z-experimental-next-news/components/story.js
Normal file
80
examples/z-experimental-next-news/components/story.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
import Link from 'next/link'
|
||||
import timeAgo from '../lib/time-ago'
|
||||
import parse from 'url-parse'
|
||||
|
||||
export default ({ id, title, date, url, user, score, commentsCount }) => {
|
||||
const { host } = parse(url)
|
||||
return (
|
||||
<div>
|
||||
<div className='title'>
|
||||
{url ? (
|
||||
<a href={url}>{title}</a>
|
||||
) : (
|
||||
<Link href='/item/[id]' as={`/item/${id}`}>
|
||||
<a>{title}</a>
|
||||
</Link>
|
||||
)}
|
||||
{url && (
|
||||
<span className='source'>
|
||||
<a href={`http://${host}`}>{host.replace(/^www\./, '')}</a>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className='meta'>
|
||||
{score} {plural(score, 'point')} by{' '}
|
||||
<Link href='/user/[id]' as={`/user/${user}`}>
|
||||
<a>{user}</a>
|
||||
</Link>{' '}
|
||||
<Link href='/item/[id]' as={`/item/${id}`}>
|
||||
<a>
|
||||
{timeAgo(new Date(date)) /* note: we re-hydrate due to ssr */} ago
|
||||
</a>
|
||||
</Link>{' '}
|
||||
|{' '}
|
||||
<Link href='/item/[id]' as={`/item/${id}`}>
|
||||
<a>
|
||||
{commentsCount} {plural(commentsCount, 'comment')}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.title {
|
||||
font-size: 15px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.title > a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title > a:visited {
|
||||
color: #828282;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.source {
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.source a,
|
||||
.meta a {
|
||||
color: #828282;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.source a:hover,
|
||||
.meta a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const plural = (n, s) => s + (n === 0 || n > 1 ? 's' : '')
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Story from './story'
|
||||
import { observe } from '../lib/get-item'
|
||||
|
||||
export default class extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = props
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.unsubscribe = observe(this.props.id, data => this.setState(data))
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.unsubscribe()
|
||||
}
|
||||
|
||||
render () {
|
||||
return <Story {...this.state} />
|
||||
}
|
||||
}
|
17
examples/z-experimental-next-news/lib/db.js
Normal file
17
examples/z-experimental-next-news/lib/db.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import firebase from 'firebase'
|
||||
|
||||
try {
|
||||
firebase.initializeApp({
|
||||
databaseURL: 'https://hacker-news.firebaseio.com'
|
||||
})
|
||||
} catch (err) {
|
||||
// we skip the "already exists" message which is
|
||||
// not an actual error when we're hot-reloading
|
||||
if (!/already exists/.test(err.message)) {
|
||||
console.error('Firebase initialization error', err.stack)
|
||||
}
|
||||
}
|
||||
|
||||
const root = firebase.database().ref('v0')
|
||||
|
||||
export default root
|
22
examples/z-experimental-next-news/lib/get-comments.js
Normal file
22
examples/z-experimental-next-news/lib/get-comments.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import db from './db'
|
||||
|
||||
// hydrate comments based on an array of item ids
|
||||
export default function fetch (ids) {
|
||||
return Promise.all(
|
||||
ids.map(async id => {
|
||||
const item = await db
|
||||
.child('item')
|
||||
.child(id)
|
||||
.once('value')
|
||||
const val = item.val()
|
||||
return {
|
||||
id: val.id,
|
||||
user: val.by,
|
||||
text: val.text,
|
||||
date: new Date(val.time * 1000),
|
||||
comments: await fetch(val.kids || []),
|
||||
commentsCount: val.descendants
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
36
examples/z-experimental-next-news/lib/get-item.js
Normal file
36
examples/z-experimental-next-news/lib/get-item.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import db from './db'
|
||||
|
||||
export default async id => {
|
||||
const item = await db
|
||||
.child('item')
|
||||
.child(id)
|
||||
.once('value')
|
||||
const val = item.val()
|
||||
if (val) {
|
||||
return transform(val)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function observe (id, fn) {
|
||||
const onval = data => fn(transform(data.val()))
|
||||
const item = db.child('item').child(id)
|
||||
item.on('value', onval)
|
||||
return () => item.off('value', onval)
|
||||
}
|
||||
|
||||
export function transform (val) {
|
||||
return {
|
||||
id: val.id,
|
||||
url: val.url,
|
||||
user: val.by,
|
||||
// time is seconds since epoch, not ms
|
||||
date: new Date(val.time * 1000),
|
||||
// sometimes `kids` is `undefined`
|
||||
comments: val.kids || [],
|
||||
commentsCount: val.descendants,
|
||||
score: val.score,
|
||||
title: val.title
|
||||
}
|
||||
}
|
20
examples/z-experimental-next-news/lib/get-stories.js
Normal file
20
examples/z-experimental-next-news/lib/get-stories.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import db from './db'
|
||||
import { transform } from './get-item'
|
||||
|
||||
export default async (type = 'topstories', { page = 1, max = 30 } = {}) => {
|
||||
const start = max * (page - 1)
|
||||
const end = start + max - 1
|
||||
const ids = await db.child(type).once('value')
|
||||
const stories = await Promise.all(
|
||||
ids
|
||||
.val()
|
||||
.slice(start, end)
|
||||
.map(id =>
|
||||
db
|
||||
.child('item')
|
||||
.child(id)
|
||||
.once('value')
|
||||
)
|
||||
)
|
||||
return stories.map(obj => transform(obj.val()))
|
||||
}
|
12
examples/z-experimental-next-news/lib/time-ago.js
Normal file
12
examples/z-experimental-next-news/lib/time-ago.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import ms from 'ms'
|
||||
|
||||
const map = {
|
||||
s: 'seconds',
|
||||
ms: 'milliseconds',
|
||||
m: 'minutes',
|
||||
h: 'hours',
|
||||
d: 'days'
|
||||
}
|
||||
|
||||
export default date =>
|
||||
ms(new Date() - date).replace(/[a-z]+/, str => ' ' + map[str])
|
3
examples/z-experimental-next-news/now.json
Normal file
3
examples/z-experimental-next-news/now.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"builds": [{ "src": "package.json", "use": "@now/next@canary" }]
|
||||
}
|
18
examples/z-experimental-next-news/package.json
Normal file
18
examples/z-experimental-next-news/package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "next-news",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"firebase": "^7.2.1",
|
||||
"ms": "^2.1.2",
|
||||
"next": "^9.1.2-canary.7",
|
||||
"nprogress": "^0.2.0",
|
||||
"react": "^16.10.2",
|
||||
"react-dom": "^16.10.2",
|
||||
"url-parse": "^1.4.7"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
}
|
||||
}
|
22
examples/z-experimental-next-news/pages/ask.js
Normal file
22
examples/z-experimental-next-news/pages/ask.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Page from '../components/page'
|
||||
import Stories from '../components/stories'
|
||||
import getStories from '../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps () {
|
||||
const page = 1
|
||||
const stories = await getStories('askstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function Ask ({ page, stories }) {
|
||||
const offset = (page - 1) * 30
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} offset={offset} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default Ask
|
22
examples/z-experimental-next-news/pages/index.js
Normal file
22
examples/z-experimental-next-news/pages/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Page from '../components/page'
|
||||
import Stories from '../components/stories'
|
||||
import getStories from '../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps () {
|
||||
const page = 1
|
||||
const stories = await getStories('topstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function Home ({ page, stories }) {
|
||||
const offset = (page - 1) * 30
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} offset={offset} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
27
examples/z-experimental-next-news/pages/item/[id].js
Normal file
27
examples/z-experimental-next-news/pages/item/[id].js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import Page from '../../components/page'
|
||||
import Item from '../../components/item'
|
||||
import getItem from '../../lib/get-item'
|
||||
import getComments from '../../lib/get-comments'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps ({ params }) {
|
||||
const story = await getItem(params.id)
|
||||
return { props: { story } }
|
||||
}
|
||||
|
||||
function ItemPage ({ story }) {
|
||||
const [comments, setComments] = useState(null)
|
||||
useEffect(() => {
|
||||
getComments(story.comments)
|
||||
.then(comments => setComments(comments))
|
||||
.catch(err => console.error(err))
|
||||
}, [])
|
||||
return (
|
||||
<Page>
|
||||
<Item story={story} comments={comments} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default ItemPage
|
21
examples/z-experimental-next-news/pages/jobs.js
Normal file
21
examples/z-experimental-next-news/pages/jobs.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react'
|
||||
import Page from '../components/page'
|
||||
import Stories from '../components/stories'
|
||||
import getStories from '../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps () {
|
||||
const page = 1
|
||||
const stories = await getStories('jobstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function Jobs ({ page, stories }) {
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default Jobs
|
7
examples/z-experimental-next-news/pages/login.js
Normal file
7
examples/z-experimental-next-news/pages/login.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import LoginForm from '../components/login-form'
|
||||
|
||||
function Login () {
|
||||
return <LoginForm />
|
||||
}
|
||||
|
||||
export default Login
|
22
examples/z-experimental-next-news/pages/newest.js
Normal file
22
examples/z-experimental-next-news/pages/newest.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Page from '../components/page'
|
||||
import Stories from '../components/stories'
|
||||
import getStories from '../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps () {
|
||||
const page = 1
|
||||
const stories = await getStories('newstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function Newest ({ page, stories }) {
|
||||
const offset = (page - 1) * 30
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} offset={offset} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default Newest
|
22
examples/z-experimental-next-news/pages/news/[id].js
Normal file
22
examples/z-experimental-next-news/pages/news/[id].js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Page from '../../components/page'
|
||||
import Stories from '../../components/stories'
|
||||
import getStories from '../../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps ({ params }) {
|
||||
const page = Number(params.id)
|
||||
const stories = await getStories('topstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function News ({ page, stories }) {
|
||||
const offset = (page - 1) * 30
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} offset={offset} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default News
|
22
examples/z-experimental-next-news/pages/show.js
Normal file
22
examples/z-experimental-next-news/pages/show.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import Page from '../components/page'
|
||||
import Stories from '../components/stories'
|
||||
import getStories from '../lib/get-stories'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getStaticProps () {
|
||||
const page = 1
|
||||
const stories = await getStories('showstories', { page })
|
||||
return { props: { page, stories } }
|
||||
}
|
||||
|
||||
function Show ({ page, stories }) {
|
||||
const offset = (page - 1) * 30
|
||||
return (
|
||||
<Page>
|
||||
<Stories page={page} offset={offset} stories={stories} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default Show
|
9
examples/z-experimental-next-news/pages/submit.js
Normal file
9
examples/z-experimental-next-news/pages/submit.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
import LoginForm from '../components/login-form'
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<p>You have to be logged in to submit</p>
|
||||
<LoginForm />
|
||||
</div>
|
||||
)
|
1
examples/z-experimental-next-news/pages/user/[id].js
Normal file
1
examples/z-experimental-next-news/pages/user/[id].js
Normal file
|
@ -0,0 +1 @@
|
|||
export default () => <>user page (WIP)</>
|
Loading…
Reference in a new issue