forked from tanner/qotnews
fun.
This commit is contained in:
parent
60e34935ee
commit
8115d86335
52
webapp/src/components/Article.svelte
Normal file
52
webapp/src/components/Article.svelte
Normal file
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
import StoryInfo from "../components/StoryInfo.svelte";
|
||||
export let story;
|
||||
|
||||
let host = new URL(story.url || story.link).hostname.replace(/^www\./, "");
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.article-title {
|
||||
text-align: justify;
|
||||
}
|
||||
.article-header {
|
||||
padding: 0 0 1rem;
|
||||
}
|
||||
.article-body {
|
||||
max-width: 45rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.article-body :global(figure) {
|
||||
margin: 0;
|
||||
}
|
||||
.article-body :global(figcaption p),
|
||||
.article-body :global(figcaption) {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.article-body :global(figcaption) {
|
||||
font-style: italic;
|
||||
margin: 0 1rem;
|
||||
font-size: 0.9em;
|
||||
text-align: justify;
|
||||
}
|
||||
.article-body :global(figure),
|
||||
.article-body :global(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<article class="article">
|
||||
<header class="article-header">
|
||||
<h1 class="article-title">{story.title}</h1>
|
||||
{#if story.url}
|
||||
<div>source: <a href={story.url}>{host}</a></div>
|
||||
{/if}
|
||||
<StoryInfo class="article-byline" {story} />
|
||||
</header>
|
||||
|
||||
<section class="article-body">
|
||||
{@html story.text}
|
||||
</section>
|
||||
</article>
|
|
@ -1,12 +1,9 @@
|
|||
<script>
|
||||
import fromUnixTime from "date-fns/fromUnixTime";
|
||||
import formatDistanceToNow from "date-fns/formatDistanceToNow";
|
||||
import Time from "../components/Time.svelte";
|
||||
|
||||
export let story;
|
||||
export let comment;
|
||||
export let showComments = true;
|
||||
export let dateString = formatDistanceToNow(fromUnixTime(comment.date), {
|
||||
addSuffix: true,
|
||||
});
|
||||
const author = (comment.author || "").replace(" ", "");
|
||||
export let id = `${author}-${comment.date}`;
|
||||
|
||||
|
@ -80,9 +77,7 @@
|
|||
class={comment.author === story.author ? 'comment-author is-op' : 'comment-author'}>{comment.author || '[Deleted]'}</span>
|
||||
•
|
||||
<a class="time-link" href="{story.id}#comment-{id}">
|
||||
<time
|
||||
datetime={fromUnixTime(comment.date).toISOString()}
|
||||
title={fromUnixTime(comment.date)}>{dateString}</time>
|
||||
<Time date={comment.date} />
|
||||
</a>
|
||||
{#if comment.comments.length}
|
||||
<button
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
<script>
|
||||
import { debounce } from 'lodash';
|
||||
import { goto, prefetch } from "@sapper/app";
|
||||
import { stores } from "@sapper/app";
|
||||
|
||||
import { debounce } from "lodash";
|
||||
import { goto, prefetch, stores } from "@sapper/app";
|
||||
export let segment;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
let q;
|
||||
let search;
|
||||
|
||||
page.subscribe((value) => {
|
||||
q = value.query.q || "";
|
||||
let handleSearch = debounce(_handleSearch, 300, {
|
||||
trailing: true,
|
||||
leading: false,
|
||||
});
|
||||
|
||||
let handleSearch = debounce(_handleSearch);
|
||||
page.subscribe((page) => {
|
||||
setTimeout(() => {
|
||||
if (segment === "search") {
|
||||
search && search.focus();
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
async function _handleSearch(event) {
|
||||
const url = `/search?q=${event.target.value}`;
|
||||
|
@ -23,35 +28,12 @@
|
|||
</script>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
border-bottom: 1px solid rgba(255, 62, 0, 0.1);
|
||||
font-weight: 300;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* clearfix */
|
||||
ul::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
[aria-current] {
|
||||
.navigation [aria-current] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
[aria-current]::after {
|
||||
.navigation [aria-current]::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: calc(100% - 1em);
|
||||
|
@ -61,38 +43,78 @@
|
|||
bottom: -1px;
|
||||
}
|
||||
|
||||
a {
|
||||
.navigation {
|
||||
border-bottom: 1px solid rgba(255, 62, 0, 0.1);
|
||||
font-weight: 300;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navigation-container {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
max-width: 64rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.navigation-container > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.navigation-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.navigation-item {
|
||||
list-style: none;
|
||||
}
|
||||
.navigation-text,
|
||||
.navigation-link {
|
||||
text-decoration: none;
|
||||
padding: 1em 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input {
|
||||
.navigation-input {
|
||||
line-height: 2;
|
||||
margin: 1em;
|
||||
vertical-align: middle;
|
||||
width: 15rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
aria-current={segment === undefined ? 'page' : undefined}
|
||||
rel="prefetch"
|
||||
href=".">News</a>
|
||||
</li>
|
||||
<li>
|
||||
<form action="/search" method="GET" rel="prefetch">
|
||||
<input
|
||||
id="search"
|
||||
bind:this={search}
|
||||
type="text"
|
||||
name="q"
|
||||
value={q}
|
||||
placeholder="Search..."
|
||||
on:keypress={handleSearch} />
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
<nav class="navigation">
|
||||
<div class="navigation-container">
|
||||
<ul class="navigation-list" role="menubar">
|
||||
<li class="navigation-item">
|
||||
<a
|
||||
class="navigation-link"
|
||||
aria-current={segment === undefined ? 'page' : undefined}
|
||||
rel="prefetch"
|
||||
href=".">News</a>
|
||||
</li>
|
||||
<li class="navigation-item">
|
||||
<a
|
||||
class="navigation-link"
|
||||
aria-current={segment === 'search' ? 'page' : undefined}
|
||||
rel="prefetch"
|
||||
href="/search">Search</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form action="/search" method="GET" rel="prefetch" role="search">
|
||||
<input
|
||||
class="navigation-input"
|
||||
id="search"
|
||||
bind:this={search}
|
||||
type="text"
|
||||
name="q"
|
||||
value={$page.query.q || ''}
|
||||
placeholder="Search..."
|
||||
on:keypress={handleSearch} />
|
||||
</form>
|
||||
<ul class="navigation-list">
|
||||
<li class="navigation-item"><span class="navigation-text">Qot.</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
55
webapp/src/components/Pagination.svelte
Normal file
55
webapp/src/components/Pagination.svelte
Normal file
|
@ -0,0 +1,55 @@
|
|||
<script>
|
||||
import { stores } from "@sapper/app";
|
||||
export let href;
|
||||
export let search;
|
||||
export let count;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
let skip = 0;
|
||||
let limit = 20;
|
||||
let prevLink = "";
|
||||
let nextLink = "";
|
||||
|
||||
page.subscribe((p) => {
|
||||
count = Number(count);
|
||||
skip = Number(p.query.skip) || 0;
|
||||
limit = Number(p.query.limit) || 20;
|
||||
|
||||
let previous = new URLSearchParams(search || "");
|
||||
let next = new URLSearchParams(search || "");
|
||||
|
||||
previous.append("skip", skip - Math.min(skip, limit));
|
||||
previous.append("limit", limit);
|
||||
|
||||
next.append("skip", skip + limit);
|
||||
next.append("limit", limit);
|
||||
|
||||
prevLink = href + "?" + previous.toString();
|
||||
nextLink = href + "?" + next.toString();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pagination {
|
||||
margin: 3rem 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.pagination-link.is-next {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="pagination">
|
||||
{#if skip > 0}
|
||||
<a
|
||||
class="pagination-link is-prev"
|
||||
href={prevLink}
|
||||
rel="prefetch">Previous</a>
|
||||
{/if}
|
||||
{#if count >= limit}
|
||||
<a class="pagination-link is-next" href={nextLink} rel="prefetch">Next</a>
|
||||
{/if}
|
||||
</div>
|
|
@ -1,26 +1,18 @@
|
|||
<script>
|
||||
import fromUnixTime from "date-fns/fromUnixTime";
|
||||
import formatDistanceToNow from "date-fns/formatDistanceToNow";
|
||||
import Time from "../components/Time.svelte";
|
||||
export let story;
|
||||
export let dateString = formatDistanceToNow(fromUnixTime(story.date), {
|
||||
addSuffix: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="info">
|
||||
<time
|
||||
datetime={fromUnixTime(story.date).toISOString()}
|
||||
title={fromUnixTime(story.date)}>{dateString}</time>
|
||||
{#if story.author && story.author_link}
|
||||
by
|
||||
<a href={story.author_link}>{story.author}</a>
|
||||
{:else if story.author}by {story.author}{/if}
|
||||
on
|
||||
<a href={story.url}>{story.source}</a>
|
||||
{#if story.score}• {story.score} points{/if}
|
||||
{#if story.num_comments}
|
||||
•
|
||||
<a rel="prefetch" href="/{story.id}#comments">{story.num_comments}
|
||||
comments</a>
|
||||
{/if}
|
||||
</div>
|
||||
<Time date={story.date} />
|
||||
{#if story.author && story.author_link}
|
||||
by
|
||||
<a href={story.author_link}>{story.author}</a>
|
||||
{:else if story.author}by {story.author}{/if}
|
||||
on
|
||||
<a href={story.link || story.url}>{story.source}</a>
|
||||
{#if story.score}• {story.score} points{/if}
|
||||
{#if story.num_comments}
|
||||
•
|
||||
<a rel="prefetch" href="/{story.id}#comments">{story.num_comments}
|
||||
comments</a>
|
||||
{/if}
|
||||
|
|
55
webapp/src/components/StoryList.svelte
Normal file
55
webapp/src/components/StoryList.svelte
Normal file
|
@ -0,0 +1,55 @@
|
|||
<script>
|
||||
import { getLogoUrl } from "../utils/logos.js";
|
||||
import StoryInfo from "../components/StoryInfo.svelte";
|
||||
export let stories;
|
||||
|
||||
const host = (url) => new URL(url).hostname.replace(/^www\./, "");
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.story-item {
|
||||
margin: 0.5rem 0 0;
|
||||
padding-left: 1.2em;
|
||||
}
|
||||
.story-icon,
|
||||
.story-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.story-icon {
|
||||
margin-left: -1.2rem;
|
||||
}
|
||||
.story-source::before {
|
||||
content: "(";
|
||||
}
|
||||
.story-source::after {
|
||||
content: ")";
|
||||
}
|
||||
|
||||
.story-item :global(a) {
|
||||
text-decoration: none;
|
||||
}
|
||||
.story-item :global(a:hover) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#each stories as story}
|
||||
<article class="story-item">
|
||||
<header class="story-header">
|
||||
<img
|
||||
src={getLogoUrl(story)}
|
||||
alt="logo"
|
||||
class="story-icon"
|
||||
style="height: 1rem; width: 1rem;" />
|
||||
<a class="story-title" rel="prefetch" href="/{story.id}">{story.title}</a>
|
||||
<a
|
||||
class="story-source"
|
||||
href={story.url || story.link}>{host(story.url || story.link)}</a>
|
||||
</header>
|
||||
<section class="story-info">
|
||||
<StoryInfo {story} />
|
||||
</section>
|
||||
</article>
|
||||
{/each}
|
||||
|
||||
<slot />
|
11
webapp/src/components/Time.svelte
Normal file
11
webapp/src/components/Time.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script>
|
||||
import fromUnixTime from "date-fns/fromUnixTime";
|
||||
import formatDistanceToNow from "date-fns/formatDistanceToNow";
|
||||
export let date;
|
||||
let d = fromUnixTime(date);
|
||||
let datetime = d.toISOString();
|
||||
let title = d.toLocaleString();
|
||||
let dateString = formatDistanceToNow(d, { addSuffix: true });
|
||||
</script>
|
||||
|
||||
<time {datetime} {title}>{dateString}</time>
|
|
@ -1,6 +1,5 @@
|
|||
import fetch from 'isomorphic-fetch';
|
||||
|
||||
// const API_URL = process.env.API_URL || 'http://news.1j.nz';
|
||||
const API_URL = process.env.API_URL || 'http://localhost:33842';
|
||||
|
||||
export async function get(req, res) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<script>
|
||||
import fromUnixTime from "date-fns/fromUnixTime";
|
||||
import Comment from "../components/Comment.svelte";
|
||||
import StoryInfo from "../components/StoryInfo.svelte";
|
||||
import Article from "../components/Article.svelte";
|
||||
export let story;
|
||||
export let related;
|
||||
|
||||
|
@ -23,25 +23,13 @@
|
|||
</script>
|
||||
|
||||
<style>
|
||||
/* .article {
|
||||
}
|
||||
.article-header {
|
||||
}
|
||||
|
||||
.article-header .article-title {
|
||||
}
|
||||
|
||||
.article-header .article-byline {
|
||||
}
|
||||
.article-body {
|
||||
} */
|
||||
.article-body :global(img) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
margin: 3rem 0;
|
||||
}
|
||||
.single {
|
||||
max-width: 56rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -54,41 +42,37 @@
|
|||
<meta property="article:author" content={story.author || story.source} />
|
||||
</svelte:head>
|
||||
|
||||
<article class="article">
|
||||
<header class="article-header">
|
||||
<h1 class="article-title">{story.title}</h1>
|
||||
<StoryInfo class="article-byline" {story} />
|
||||
</header>
|
||||
<section class="single">
|
||||
<Article {story} />
|
||||
|
||||
<section class="article-body">
|
||||
{@html story.text}
|
||||
</section>
|
||||
</article>
|
||||
{#if hasComments}
|
||||
<hr class="spacer" />
|
||||
|
||||
{#if hasComments}
|
||||
<hr class="spacer" />
|
||||
<section id="comments">
|
||||
<header>
|
||||
<h2>Comments</h2>
|
||||
|
||||
<section class="comments" id="comments">
|
||||
<header>
|
||||
<h2>Comments</h2>
|
||||
|
||||
{#if others.length}
|
||||
<h3>
|
||||
Other discussions:
|
||||
{#each others as r}
|
||||
{#if r.num_comments}
|
||||
<a href="/{r.id}#comments" rel="prefetch">{r.source}</a>
|
||||
{/if}
|
||||
{#if others.length}
|
||||
<h3>
|
||||
Other discussions:
|
||||
{#each others as r}
|
||||
{#if r.num_comments}
|
||||
<a href="/{r.id}#comments" rel="prefetch">
|
||||
{r.source}
|
||||
({r.num_comments})
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
</h3>
|
||||
{/if}
|
||||
</header>
|
||||
{#if story.comments.length}
|
||||
<div class="comments">
|
||||
{#each story.comments as comment}
|
||||
<Comment {story} {comment} />
|
||||
{/each}
|
||||
</h3>
|
||||
</div>
|
||||
{/if}
|
||||
</header>
|
||||
{#if story.comments.length}
|
||||
<div class="comments">
|
||||
{#each story.comments as comment}
|
||||
<Comment {story} {comment} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</section>
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
<style>
|
||||
main {
|
||||
position: relative;
|
||||
max-width: 56em;
|
||||
max-width: 64rem;
|
||||
background-color: white;
|
||||
padding: 2em;
|
||||
padding: 0.5rem;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import fetch from 'isomorphic-fetch';
|
||||
|
||||
const API_URL = process.env.API_URL || 'http://news.1j.nz';
|
||||
// const API_URL = process.env.API_URL || 'http://localhost:33842';
|
||||
const API_URL = process.env.API_URL || 'http://localhost:33842';
|
||||
|
||||
export async function get(req, res) {
|
||||
const { skip, limit } = {
|
||||
|
|
|
@ -16,74 +16,18 @@
|
|||
</script>
|
||||
|
||||
<script>
|
||||
import { stores } from "@sapper/app";
|
||||
import { getLogoUrl } from "../utils/logos.js";
|
||||
import StoryInfo from "../components/StoryInfo.svelte";
|
||||
import StoryList from "../components/StoryList.svelte";
|
||||
import Pagination from "../components/Pagination.svelte";
|
||||
|
||||
export let stories;
|
||||
export let skip;
|
||||
export let limit;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
page.subscribe((value) => {
|
||||
skip = value.query.skip || 0;
|
||||
limit = value.query.limit || 20;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
article {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: 3rem 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.pagination-link.is-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>QotNews</title>
|
||||
<meta property="og:title" content="QotNews" />
|
||||
<meta property="og:type" content="website" />
|
||||
</svelte:head>
|
||||
|
||||
<section>
|
||||
{#each stories as story}
|
||||
<article>
|
||||
<header>
|
||||
<img
|
||||
src={getLogoUrl(story)}
|
||||
alt="logo"
|
||||
style="height: 1rem; width: 1rem;" />
|
||||
<a rel="prefetch" href="/{story.id}">{story.title}</a>
|
||||
(<a
|
||||
href={story.url || story.link}>{new URL(story.url || story.link).hostname.replace(/^www\./, '')}</a>)
|
||||
</header>
|
||||
<section>
|
||||
<StoryInfo {story} />
|
||||
</section>
|
||||
</article>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<div class="pagination">
|
||||
{#if Number(skip) > 0}
|
||||
<a
|
||||
class="pagination-link is-left"
|
||||
href="?skip={Number(skip) - Math.min(Number(skip), Number(limit))}&limit={limit}"
|
||||
rel="prefetch">Previous</a>
|
||||
{/if}
|
||||
{#if stories.length == Number(limit)}
|
||||
<a
|
||||
class="pagination-link is-right"
|
||||
href="?skip={Number(skip) + Number(limit)}&limit={limit}"
|
||||
rel="prefetch">Next</a>
|
||||
{/if}
|
||||
</div>
|
||||
<StoryList {stories}>
|
||||
<Pagination href="/" count={stories.length} />
|
||||
</StoryList>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import fetch from 'isomorphic-fetch';
|
||||
|
||||
const API_URL = process.env.API_URL || 'http://news.1j.nz';
|
||||
const API_URL = process.env.API_URL || 'http://localhost:33842';
|
||||
|
||||
export async function get(req, res) {
|
||||
const { skip, limit } = {
|
||||
|
|
|
@ -20,84 +20,23 @@
|
|||
|
||||
<script>
|
||||
import { stores } from "@sapper/app";
|
||||
import { getLogoUrl } from "../utils/logos.js";
|
||||
import StoryInfo from "../components/StoryInfo.svelte";
|
||||
import StoryList from "../components/StoryList.svelte";
|
||||
import Pagination from "../components/Pagination.svelte";
|
||||
|
||||
export let stories;
|
||||
export let skip;
|
||||
export let limit;
|
||||
export let q;
|
||||
|
||||
const { page } = stores();
|
||||
|
||||
page.subscribe((value) => {
|
||||
skip = value.query.skip || 0;
|
||||
limit = value.query.limit || 20;
|
||||
q = value.query.query || "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
article {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: 3rem 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.pagination-link {
|
||||
/* border: solid 1px #aaa;
|
||||
border-radius: 0;
|
||||
background: #f1f1f1;
|
||||
border-radius: 5px;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
text-decoration: none; */
|
||||
}
|
||||
.pagination-link.is-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>QotNews</title>
|
||||
<meta property="og:title" content="QotNews" />
|
||||
<meta property="og:type" content="website" />
|
||||
</svelte:head>
|
||||
|
||||
<section>
|
||||
{#each stories as story}
|
||||
<article>
|
||||
<header>
|
||||
<img
|
||||
src={getLogoUrl(story)}
|
||||
alt="logo"
|
||||
style="height: 1rem; width: 1rem;" />
|
||||
<a rel="prefetch" href="/{story.id}">{story.title}</a>
|
||||
(<a
|
||||
href={story.url || story.link}>{new URL(story.url || story.link).hostname.replace(/^www\./, '')}</a>)
|
||||
</header>
|
||||
<section>
|
||||
<StoryInfo {story} />
|
||||
</section>
|
||||
</article>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<div class="pagination">
|
||||
{#if Number(skip) > 0}
|
||||
<a
|
||||
class="pagination-link is-left"
|
||||
href="?skip={Number(skip) - Math.min(Number(skip), Number(limit))}&limit={limit}"
|
||||
rel="prefetch">Previous</a>
|
||||
{/if}
|
||||
{#if stories.length == Number(limit)}
|
||||
<a
|
||||
class="pagination-link is-right"
|
||||
href="?skip={Number(skip) + Number(limit)}&limit={limit}"
|
||||
rel="prefetch">Next</a>
|
||||
{/if}
|
||||
</div>
|
||||
<StoryList {stories}>
|
||||
<Pagination
|
||||
href="/search"
|
||||
search="q={$page.query.q}"
|
||||
count={stories.length} />
|
||||
</StoryList>
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
|
||||
margin-bottom: 50vh;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0 0 0.5em 0;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0 0 0.5em 0;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: menlo, inconsolata, monospace;
|
||||
font-size: calc(1em - 2px);
|
||||
color: #555;
|
||||
background-color: #f0f0f0;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 2px;
|
||||
font-family: menlo, inconsolata, monospace;
|
||||
font-size: calc(1em - 2px);
|
||||
color: #555;
|
||||
background-color: #f0f0f0;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,12 @@ import Article from './pages/Article.js';
|
|||
import Comments from './pages/Comments.js';
|
||||
import Results from './pages/Results.js';
|
||||
|
||||
const pagingKey = (props) => {
|
||||
const query = new URLSearchParams(props.location.search);
|
||||
const skip = query.get('skip') || 0;
|
||||
const limit = query.get('limit') || 20;
|
||||
return `skip=${skip}&limit=${limit}`;
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -66,7 +72,7 @@ class App extends React.Component {
|
|||
<Route path='/(|search)' component={Submit} />
|
||||
</div>
|
||||
|
||||
<Route path='/' exact render={(props) => <Feed {...props} updateCache={this.updateCache} key={Feed.key(props)} />} />
|
||||
<Route path='/' exact render={(props) => <Feed {...props} updateCache={this.updateCache} key={pagingKey(props)} />} />
|
||||
<Switch>
|
||||
<Route path='/search' component={Results} />
|
||||
<Route path='/:id' exact render={(props) => <Article {...props} cache={this.cache} />} />
|
||||
|
|
|
@ -76,11 +76,4 @@ class Feed extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
Feed.key = function (props) {
|
||||
const query = new URLSearchParams(props.location.search);
|
||||
const skip = query.get('skip') || 0;
|
||||
const limit = query.get('limit') || 20;
|
||||
return `skip=${skip}&limit=${limit}`;
|
||||
}
|
||||
|
||||
export default Feed;
|
||||
|
|
Loading…
Reference in New Issue
Block a user