Adding articles preview to the main homepage, changing the API for articles to return with cap on max pages and max articles env variables, and formatting code text.

This commit is contained in:
Bradley Shellnut 2023-02-15 16:12:30 -08:00
parent b92de5c112
commit a309229c51
8 changed files with 142 additions and 24 deletions

View file

@ -15,6 +15,7 @@
}, },
"devDependencies": { "devDependencies": {
"@iconify-icons/material-symbols": "^1.2.27", "@iconify-icons/material-symbols": "^1.2.27",
"@iconify-icons/mdi": "^1.2.41",
"@iconify-icons/radix-icons": "^1.2.8", "@iconify-icons/radix-icons": "^1.2.8",
"@iconify-icons/simple-icons": "^1.2.42", "@iconify-icons/simple-icons": "^1.2.42",
"@leveluptuts/svelte-side-menu": "^1.0.5", "@leveluptuts/svelte-side-menu": "^1.0.5",

View file

@ -2,6 +2,7 @@ lockfileVersion: 5.4
specifiers: specifiers:
'@iconify-icons/material-symbols': ^1.2.27 '@iconify-icons/material-symbols': ^1.2.27
'@iconify-icons/mdi': ^1.2.41
'@iconify-icons/radix-icons': ^1.2.8 '@iconify-icons/radix-icons': ^1.2.8
'@iconify-icons/simple-icons': ^1.2.42 '@iconify-icons/simple-icons': ^1.2.42
'@leveluptuts/svelte-side-menu': ^1.0.5 '@leveluptuts/svelte-side-menu': ^1.0.5
@ -42,6 +43,7 @@ specifiers:
devDependencies: devDependencies:
'@iconify-icons/material-symbols': 1.2.27 '@iconify-icons/material-symbols': 1.2.27
'@iconify-icons/mdi': 1.2.41
'@iconify-icons/radix-icons': 1.2.8 '@iconify-icons/radix-icons': 1.2.8
'@iconify-icons/simple-icons': 1.2.42 '@iconify-icons/simple-icons': 1.2.42
'@leveluptuts/svelte-side-menu': 1.0.5 '@leveluptuts/svelte-side-menu': 1.0.5
@ -565,6 +567,12 @@ packages:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
dev: true dev: true
/@iconify-icons/mdi/1.2.41:
resolution: {integrity: sha512-duqTSmY0H+e/LdSZD5B8PxnJfdfh6qdLVnrI6klHGSSykz23d1KdvoPpfFpgF8mWWDm4UlHIO+rrvsqMLEb3NQ==}
dependencies:
'@iconify/types': 2.0.0
dev: true
/@iconify-icons/radix-icons/1.2.8: /@iconify-icons/radix-icons/1.2.8:
resolution: {integrity: sha512-bZqRIbeqe6yNSLPgcQOyOl86C2P/apaY9Dq/BddWxitN8olbTp2MLuDJenNF+wxbQGgKkQFfm3vb6Z+4Nbhk+g==} resolution: {integrity: sha512-bZqRIbeqe6yNSLPgcQOyOl86C2P/apaY9Dq/BddWxitN8olbTp2MLuDJenNF+wxbQGgKkQFfm3vb6Z+4Nbhk+g==}
dependencies: dependencies:

View file

@ -0,0 +1,105 @@
<script lang="ts">
import OpenInNew from '@iconify-icons/mdi/open-in-new';
import type { Article } from "$root/lib/types/article";
export let articles: Article[];
export let totalArticles: number;
export let compact: boolean = false;
</script>
<div>
<h2>Favorite Articles</h2>
<div style="display: grid;">
{#each articles as article}
<article class="articleStyles card">
<section>
<h3>
<a
target="_blank"
aria-label={`Link to ${article.title}`}
href={article.url.toString()}
rel="noreferrer"
>
{#if compact}
{article.title.substring(0, 50).trim()}&#8230;
{:else}
{article.title}
{/if}
<iconify-icon icon={OpenInNew} width="24" height="24" role="img" title="Open Article Externally" />
</a>{' '}
</h3>
</section>
<section>
<p>Reading time: {article.reading_time} minutes</p>
<div class="tagsStyles">
<p>Tags:</p>
{#each article.tags as tag}
<p>{tag}</p>
{/each}
</div>
</section>
</article>
{/each}
</div>
<div class="moreArticlesStyles">
<a href="/articles">{`${totalArticles} more articles`}</a>
<a href="/articles" aria-label={`${totalArticles} more articles`}>
<iconify-icon icon="material-symbols:arrow-right-alt-rounded"></iconify-icon>
</a>
</div>
</div>
<style lang="postcss">
.articleStyles {
display: grid;
grid-template-rows: repeat(1fr, 3);
gap: 0.5rem;
margin: 1.5rem 0;
a {
overflow-wrap: anywhere;
}
p {
margin: 0.4rem 0.25rem;
}
}
.tagsStyles {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: left;
align-items: center;
p + p {
background-color: var(--linkHover);
color: var(--buttonTextColor);
padding: 0.25rem 0.5rem;
margin: 0.5rem;
border-radius: 2px;
font-size: 1.2rem;
}
}
.moreArticlesStyles {
margin: 1.7rem;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1rem;
a {
font-size: 2rem;
svg {
text-decoration: none;
}
}
@media (max-width: 1000px) {
font-size: 1.5rem;
}
}
</style>

View file

@ -1,12 +1,17 @@
import type { PageServerLoad } from './lib/$types'; import type { PageServerLoad } from './lib/$types';
import { PAGE_SIZE } from '$env/static/private';
import { fetchBandcampAlbums } from '$root/lib/util/fetchBandcampAlbums'; import { fetchBandcampAlbums } from '$root/lib/util/fetchBandcampAlbums';
export const load: PageServerLoad = async ({ setHeaders }) => { export const load: PageServerLoad = async ({ fetch, setHeaders }) => {
const albums = await fetchBandcampAlbums(); const albums = async () => await fetchBandcampAlbums();
const articles = async () => await fetch(`/api/articles?page=1&limit=3`);
// const art = articles.
// console.log(`Articles: ${JSON.stringify(await (await articles()).json())}`);
setHeaders({ setHeaders({
'cache-control': 'max-age=43200' 'cache-control': 'max-age=43200'
}); });
return { return {
albums albums: albums(),
articlesData: (await articles()).json()
}; };
}; };

View file

@ -1,12 +1,20 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import Bandcamp from '$lib/components/bandcamp/index.svelte'; import Bandcamp from '$lib/components/bandcamp/index.svelte';
import Articles from '$lib/components/articles/index.svelte';
import SEO from '$lib/components/SEO.svelte'; import SEO from '$lib/components/SEO.svelte';
import type { Album } from '$root/lib/types/album'; import type { Album } from '$lib/types/album';
import type { Article } from '$lib/types/article';
import type { ArticlePageLoad } from './articles/[page]/+page.server';
export let data: PageData; export let data: PageData;
let albums: Album[]; let albums: Album[];
$: ({ albums } = data); let articlesData: ArticlePageLoad;
let articles: Article[];
let totalArticles: number;
$: ({ albums, articlesData } = data);
$: ({ articles, totalArticles } = articlesData);
$: console.log(`All data: ${JSON.stringify(articlesData)}`);
const userNames = { const userNames = {
github: 'BradNut', github: 'BradNut',
@ -64,7 +72,7 @@
</div> </div>
<div class="social-info"> <div class="social-info">
<Bandcamp {albums} /> <Bandcamp {albums} />
<!-- <Articles /> --> <Articles {articles} {totalArticles} compact={true} />
</div> </div>
</div> </div>

View file

@ -5,7 +5,8 @@ import {
WALLABAG_PASSWORD, WALLABAG_PASSWORD,
WALLABAG_URL, WALLABAG_URL,
WALLABAG_MAX_PAGES, WALLABAG_MAX_PAGES,
PAGE_SIZE PAGE_SIZE,
WALLABAG_MAX_ARTICLES
} from '$env/static/private'; } from '$env/static/private';
import intersect from 'just-intersect'; import intersect from 'just-intersect';
import type { Article, WallabagArticle } from '$root/lib/types/article'; import type { Article, WallabagArticle } from '$root/lib/types/article';
@ -41,7 +42,7 @@ export async function fetchArticlesApi(
const pageQuery: PageQuery = { const pageQuery: PageQuery = {
sort: 'updated', sort: 'updated',
perPage: +PAGE_SIZE, perPage: +queryParams?.limit || +PAGE_SIZE,
since: 0, since: 0,
page: +queryParams?.page || 1, page: +queryParams?.page || 1,
tags: 'programming', tags: 'programming',
@ -104,25 +105,12 @@ export async function fetchArticlesApi(
} }
}); });
// if (!entries._links.next) {
// return;
// }
// console.log(`Links next ${JSON.stringify(entries._links.next)}`);
// const response = await fetch(entries._links.next.href, {
// method: 'GET',
// headers: {
// Authorization: `Bearer ${auth.access_token}`
// }
// });
// entries = await response.json();
// } while (entries._links.next);
return { return {
articles, articles,
currentPage: page, currentPage: page,
totalPages: pages <= WALLABAG_MAX_PAGES ? pages : WALLABAG_MAX_PAGES, totalPages: pages > +WALLABAG_MAX_PAGES ? +WALLABAG_MAX_PAGES : pages,
limit, limit,
totalArticles: total > limit * WALLABAG_MAX_PAGES ? limit * WALLABAG_MAX_PAGES : total, totalArticles: total > +WALLABAG_MAX_ARTICLES ? +WALLABAG_MAX_ARTICLES : total,
cacheControl cacheControl
}; };
} }

View file

@ -10,7 +10,8 @@ export const GET: RequestHandler = async ({ url, setHeaders }: RequestEvent) =>
throw new Error('Page does not exist'); throw new Error('Page does not exist');
} }
const response = await fetchArticlesApi('get', `fetchArticles`, { const response = await fetchArticlesApi('get', `fetchArticles`, {
page page,
limit: url?.searchParams?.get('limit') || '6'
}); });
if (response?.articles) { if (response?.articles) {
@ -26,6 +27,7 @@ export const GET: RequestHandler = async ({ url, setHeaders }: RequestEvent) =>
} }
} }
console.log(`API response ${JSON.stringify(response)}`);
return json(response); return json(response);
} }
} catch (e) { } catch (e) {

View file

@ -21,6 +21,7 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
const resp = await fetch(`/api/articles?page=${page}`); const resp = await fetch(`/api/articles?page=${page}`);
const { articles, currentPage, totalPages, limit, totalArticles }: ArticlePageLoad = const { articles, currentPage, totalPages, limit, totalArticles }: ArticlePageLoad =
await resp.json(); await resp.json();
return { return {
articles, articles,
currentPage, currentPage,