forked from tanner/qotnews
		
	svelte.
This commit is contained in:
		| @@ -7,7 +7,7 @@ | ||||
|   export let dateString = formatDistanceToNow(fromUnixTime(comment.date), { | ||||
|     addSuffix: true, | ||||
|   }); | ||||
|   const author = comment.author.replace(" ", ""); | ||||
|   const author = (comment.author || "").replace(" ", ""); | ||||
|   export let id = `${author}-${comment.date}`; | ||||
|  | ||||
|   export function toggleComments() { | ||||
| @@ -16,45 +16,98 @@ | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|   .comment { | ||||
|     margin: 0.5rem 0; | ||||
|   } | ||||
|   .comment:not(:first-of-type) { | ||||
|     margin: 0.5rem 0; | ||||
|     border-top: solid 1px #ddd; | ||||
|     padding: 0.5rem 0 0; | ||||
|   } | ||||
|   .comment-info { | ||||
|     color: #222; | ||||
|   } | ||||
|   .comment-author { | ||||
|     font-weight: 600; | ||||
|     padding: 0 0.4em 0.2em; | ||||
|     border-radius: 0.5em; | ||||
|     background: #f1f1f1; | ||||
|     color: #000; | ||||
|   } | ||||
|   .comment-author.is-op { | ||||
|     background: #333; | ||||
|     color: #fff; | ||||
|   } | ||||
|   .comment-text { | ||||
|     padding: 0 0.5rem; | ||||
|     color: #333; | ||||
|     color: #000; | ||||
|   } | ||||
|   .comment-text.is-collapsed { | ||||
|     height: 3rem; | ||||
|     overflow: hidden; | ||||
|     color: #888; | ||||
|   } | ||||
|   .comment-text.is-collapsed::after { | ||||
|     content: "..."; | ||||
|     font-style: italic; | ||||
|   } | ||||
|   .comment-children { | ||||
|     margin-left: 1rem; | ||||
|     padding-left: 1rem; | ||||
|     margin-left: 0.5rem; | ||||
|     padding-left: 0.5rem; | ||||
|     border-left: solid 1px #000; | ||||
|   } | ||||
|   .link-button { | ||||
|   .toggle-children { | ||||
|     background: none; | ||||
|     border: none; | ||||
|     padding: 0 0.1rem; | ||||
|     padding: 0 0.25rem; | ||||
|     color: inherit; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|   .time-link { | ||||
|     text-decoration: none; | ||||
|   } | ||||
|   .time-link:hover { | ||||
|     text-decoration: underline; | ||||
|   } | ||||
|   .is-lighter { | ||||
|     color: #888; | ||||
|   } | ||||
| </style> | ||||
|  | ||||
| <div class="comment" id="comment-{id}"> | ||||
|   <div class="comment-author"> | ||||
|     {comment.author} | ||||
|     | | ||||
|     <a href="{story.id}#comment-{id}">{dateString}</a> | ||||
| <article class="comment" id="comment-{id}"> | ||||
|   <header class="comment-info"> | ||||
|     <span | ||||
|       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> | ||||
|     </a> | ||||
|     {#if comment.comments.length} | ||||
|       <button | ||||
|         class="link-button" | ||||
|         class="toggle-children" | ||||
|         on:click={toggleComments}>[{showComments ? '-' : '+'}]</button> | ||||
|     {/if} | ||||
|   </div> | ||||
|   <div class="comment-text"> | ||||
|   </header> | ||||
|  | ||||
|   <section class={showComments ? 'comment-text' : 'comment-text is-collapsed'}> | ||||
|     {@html comment.text} | ||||
|   </div> | ||||
|   {#if showComments && comment.comments.length} | ||||
|   </section> | ||||
|  | ||||
|   {#if !showComments} | ||||
|     <div class="comment-children"> | ||||
|       <button | ||||
|         class="toggle-children is-lighter" | ||||
|         on:click={toggleComments}>[expand]</button> | ||||
|     </div> | ||||
|   {/if} | ||||
|  | ||||
|   {#if showComments && comment.comments.length} | ||||
|     <footer class="comment-children"> | ||||
|       {#each comment.comments as child} | ||||
|         <svelte:self {story} comment={child} /> | ||||
|       {/each} | ||||
|     </div> | ||||
|     </footer> | ||||
|   {/if} | ||||
| </div> | ||||
| </article> | ||||
|   | ||||
| @@ -2,27 +2,25 @@ | ||||
|   import fromUnixTime from "date-fns/fromUnixTime"; | ||||
|   import formatDistanceToNow from "date-fns/formatDistanceToNow"; | ||||
|   export let story; | ||||
|  | ||||
|   export let dateString = formatDistanceToNow(fromUnixTime(story.date), { | ||||
|     addSuffix: true, | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
| </style> | ||||
|  | ||||
| <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 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> | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import fetch from 'node-fetch'; | ||||
| import fetch from 'isomorphic-fetch'; | ||||
|  | ||||
| const API_URL = process.env.API_URL || 'http://news.1j.nz'; | ||||
|  | ||||
| export async function get(req, res) { | ||||
| 	const response = await fetch(`http://localhost:33842/api/${req.params.id}`); | ||||
| 	res.writeHead(200, { | ||||
| 		'Content-Type': 'application/json' | ||||
| 	}); | ||||
| 	const response = await fetch(`${API_URL}/api/${req.params.id}`); | ||||
| 	res.writeHead(response.status, { 'Content-Type': 'application/json' }); | ||||
| 	res.end(await response.text()); | ||||
| } | ||||
| @@ -1,7 +1,5 @@ | ||||
| <script context="module"> | ||||
|   export async function preload({ params }) { | ||||
|     // the `slug` parameter is available because | ||||
|     // this file is called [slug].svelte | ||||
|     const res = await this.fetch(`${params.id}.json`); | ||||
|     const data = await res.json(); | ||||
|  | ||||
| @@ -14,6 +12,7 @@ | ||||
| </script> | ||||
|  | ||||
| <script> | ||||
|   import fromUnixTime from "date-fns/fromUnixTime"; | ||||
|   import Comment from "../components/Comment.svelte"; | ||||
|   import StoryInfo from "../components/StoryInfo.svelte"; | ||||
|   export let story; | ||||
| @@ -24,44 +23,6 @@ | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|   /* | ||||
| 		By default, CSS is locally scoped to the component, | ||||
| 		and any unused styles are dead-code-eliminated. | ||||
| 		In this page, Svelte can't know which elements are | ||||
| 		going to appear inside the {{{post.html}}} block, | ||||
| 		so we have to use the :global(...) modifier to target | ||||
| 		all elements inside .content | ||||
| 	*/ | ||||
|   .content :global(h2) { | ||||
|     font-size: 1.4em; | ||||
|     font-weight: 500; | ||||
|   } | ||||
|  | ||||
|   .content :global(pre) { | ||||
|     background-color: #f9f9f9; | ||||
|     box-shadow: inset 1px 1px 5px rgba(0, 0, 0, 0.05); | ||||
|     padding: 0.5em; | ||||
|     border-radius: 2px; | ||||
|     overflow-x: auto; | ||||
|   } | ||||
|  | ||||
|   .content :global(pre) :global(code) { | ||||
|     background-color: transparent; | ||||
|     padding: 0; | ||||
|   } | ||||
|  | ||||
|   .content :global(ul) { | ||||
|     line-height: 1.5; | ||||
|   } | ||||
|  | ||||
|   .content :global(li) { | ||||
|     margin: 0 0 0.5em 0; | ||||
|   } | ||||
|  | ||||
|   .content :global(img) { | ||||
|     max-width: 100%; | ||||
|   } | ||||
|  | ||||
|   .spacer { | ||||
|     margin: 3rem 0; | ||||
|   } | ||||
| @@ -69,33 +30,49 @@ | ||||
|  | ||||
| <svelte:head> | ||||
|   <title>{story.title}</title> | ||||
|   <meta property="og:title" content={story.title} /> | ||||
|   <meta property="og:type" content="article" /> | ||||
|   <meta | ||||
|     property="article:published_time" | ||||
|     content={fromUnixTime(story.date).toISOString()} /> | ||||
|   <meta property="article:author" content={story.author || story.source} /> | ||||
| </svelte:head> | ||||
|  | ||||
| <h1>{story.title}</h1> | ||||
| <StoryInfo {story} /> | ||||
| <article class="article"> | ||||
|   <header class="article-header"> | ||||
|     <h1 class="article-title">{story.title}</h1> | ||||
|     <StoryInfo class="article-byline" {story} /> | ||||
|   </header> | ||||
|  | ||||
| <div class="content"> | ||||
|   {@html story.text} | ||||
| </div> | ||||
|   <section class="article-body"> | ||||
|     {@html story.text} | ||||
|   </section> | ||||
| </article> | ||||
|  | ||||
| {#if hasComments} | ||||
|   <hr class="spacer" /> | ||||
|   <h2 id="comments">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} | ||||
|       {/each} | ||||
|     </h3> | ||||
|   {/if} | ||||
|   {#if story.comments.length} | ||||
|     <div class="comments"> | ||||
|       {#each story.comments as comment} | ||||
|         <Comment {story} {comment} /> | ||||
|       {/each} | ||||
|     </div> | ||||
|   {/if} | ||||
|  | ||||
|   <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} | ||||
|           {/each} | ||||
|         </h3> | ||||
|       {/if} | ||||
|     </header> | ||||
|     {#if story.comments.length} | ||||
|       <div class="comments"> | ||||
|         {#each story.comments as comment} | ||||
|           <Comment {story} {comment} /> | ||||
|         {/each} | ||||
|       </div> | ||||
|     {/if} | ||||
|   </section> | ||||
| {/if} | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import fetch from 'node-fetch'; | ||||
| import fetch from 'isomorphic-fetch'; | ||||
|  | ||||
| const API_URL = process.env.API_URL || 'http://news.1j.nz'; | ||||
|  | ||||
| export async function get(req, res) { | ||||
| 	const response = await fetch(`http://localhost:33842/api`); | ||||
| 	res.writeHead(200, { | ||||
| 		'Content-Type': 'application/json' | ||||
| 	}); | ||||
| 	const response = await fetch(`${API_URL}/api`); | ||||
| 	res.writeHead(response.status, { 'Content-Type': 'application/json' }); | ||||
| 	res.end(await response.text()); | ||||
| } | ||||
| @@ -22,7 +22,9 @@ | ||||
| </style> | ||||
|  | ||||
| <svelte:head> | ||||
|   <title>News</title> | ||||
|   <title>QotNews</title> | ||||
|   <meta property="og:title" content="QotNews" /> | ||||
|   <meta property="og:type" content="website" /> | ||||
| </svelte:head> | ||||
|  | ||||
| <section> | ||||
|   | ||||
| @@ -11,23 +11,11 @@ | ||||
| 	<link rel="manifest" href="manifest.json" crossorigin="use-credentials"> | ||||
| 	<link rel="icon" type="image/png" href="favicon.png"> | ||||
|  | ||||
| 	<!-- Sapper creates a <script> tag containing `src/client.js` | ||||
| 	     and anything else it needs to hydrate the app and | ||||
| 	     initialise the router --> | ||||
| 	%sapper.scripts% | ||||
|  | ||||
| 	<!-- Sapper generates a <style> tag containing critical CSS | ||||
| 	     for the current page. CSS for the rest of the app is | ||||
| 	     lazily loaded when it precaches secondary pages --> | ||||
| 	%sapper.styles% | ||||
|  | ||||
| 	<!-- This contains the contents of the <svelte:head> component, if | ||||
| 	     the current page has one --> | ||||
| 	%sapper.head% | ||||
| </head> | ||||
| <body> | ||||
| 	<!-- The application will be rendered inside this element, | ||||
| 	     because `src/client.js` references it --> | ||||
| 	<div id="sapper">%sapper.html%</div> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user