This commit is contained in:
Jason Schwarzenberger
2020-12-01 16:47:04 +13:00
parent 60e34935ee
commit 8115d86335
17 changed files with 352 additions and 304 deletions

View 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>

View File

@@ -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>
&bull;
<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

View File

@@ -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>

View 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>

View File

@@ -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}&bull; {story.score} points{/if}
{#if story.num_comments}
&bull;
<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}&bull; {story.score} points{/if}
{#if story.num_comments}
&bull;
<a rel="prefetch" href="/{story.id}#comments">{story.num_comments}
comments</a>
{/if}

View 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 />

View 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>