mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
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:
parent
b92de5c112
commit
a309229c51
8 changed files with 142 additions and 24 deletions
|
|
@ -15,6 +15,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@iconify-icons/material-symbols": "^1.2.27",
|
||||
"@iconify-icons/mdi": "^1.2.41",
|
||||
"@iconify-icons/radix-icons": "^1.2.8",
|
||||
"@iconify-icons/simple-icons": "^1.2.42",
|
||||
"@leveluptuts/svelte-side-menu": "^1.0.5",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ lockfileVersion: 5.4
|
|||
|
||||
specifiers:
|
||||
'@iconify-icons/material-symbols': ^1.2.27
|
||||
'@iconify-icons/mdi': ^1.2.41
|
||||
'@iconify-icons/radix-icons': ^1.2.8
|
||||
'@iconify-icons/simple-icons': ^1.2.42
|
||||
'@leveluptuts/svelte-side-menu': ^1.0.5
|
||||
|
|
@ -42,6 +43,7 @@ specifiers:
|
|||
|
||||
devDependencies:
|
||||
'@iconify-icons/material-symbols': 1.2.27
|
||||
'@iconify-icons/mdi': 1.2.41
|
||||
'@iconify-icons/radix-icons': 1.2.8
|
||||
'@iconify-icons/simple-icons': 1.2.42
|
||||
'@leveluptuts/svelte-side-menu': 1.0.5
|
||||
|
|
@ -565,6 +567,12 @@ packages:
|
|||
'@iconify/types': 2.0.0
|
||||
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:
|
||||
resolution: {integrity: sha512-bZqRIbeqe6yNSLPgcQOyOl86C2P/apaY9Dq/BddWxitN8olbTp2MLuDJenNF+wxbQGgKkQFfm3vb6Z+4Nbhk+g==}
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -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()}…
|
||||
{: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>
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
import type { PageServerLoad } from './lib/$types';
|
||||
import { PAGE_SIZE } from '$env/static/private';
|
||||
import { fetchBandcampAlbums } from '$root/lib/util/fetchBandcampAlbums';
|
||||
|
||||
export const load: PageServerLoad = async ({ setHeaders }) => {
|
||||
const albums = await fetchBandcampAlbums();
|
||||
export const load: PageServerLoad = async ({ fetch, setHeaders }) => {
|
||||
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({
|
||||
'cache-control': 'max-age=43200'
|
||||
});
|
||||
return {
|
||||
albums
|
||||
albums: albums(),
|
||||
articlesData: (await articles()).json()
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import Bandcamp from '$lib/components/bandcamp/index.svelte';
|
||||
import Articles from '$lib/components/articles/index.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;
|
||||
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 = {
|
||||
github: 'BradNut',
|
||||
|
|
@ -64,7 +72,7 @@
|
|||
</div>
|
||||
<div class="social-info">
|
||||
<Bandcamp {albums} />
|
||||
<!-- <Articles /> -->
|
||||
<Articles {articles} {totalArticles} compact={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import {
|
|||
WALLABAG_PASSWORD,
|
||||
WALLABAG_URL,
|
||||
WALLABAG_MAX_PAGES,
|
||||
PAGE_SIZE
|
||||
PAGE_SIZE,
|
||||
WALLABAG_MAX_ARTICLES
|
||||
} from '$env/static/private';
|
||||
import intersect from 'just-intersect';
|
||||
import type { Article, WallabagArticle } from '$root/lib/types/article';
|
||||
|
|
@ -41,7 +42,7 @@ export async function fetchArticlesApi(
|
|||
|
||||
const pageQuery: PageQuery = {
|
||||
sort: 'updated',
|
||||
perPage: +PAGE_SIZE,
|
||||
perPage: +queryParams?.limit || +PAGE_SIZE,
|
||||
since: 0,
|
||||
page: +queryParams?.page || 1,
|
||||
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 {
|
||||
articles,
|
||||
currentPage: page,
|
||||
totalPages: pages <= WALLABAG_MAX_PAGES ? pages : WALLABAG_MAX_PAGES,
|
||||
totalPages: pages > +WALLABAG_MAX_PAGES ? +WALLABAG_MAX_PAGES : pages,
|
||||
limit,
|
||||
totalArticles: total > limit * WALLABAG_MAX_PAGES ? limit * WALLABAG_MAX_PAGES : total,
|
||||
totalArticles: total > +WALLABAG_MAX_ARTICLES ? +WALLABAG_MAX_ARTICLES : total,
|
||||
cacheControl
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ export const GET: RequestHandler = async ({ url, setHeaders }: RequestEvent) =>
|
|||
throw new Error('Page does not exist');
|
||||
}
|
||||
const response = await fetchArticlesApi('get', `fetchArticles`, {
|
||||
page
|
||||
page,
|
||||
limit: url?.searchParams?.get('limit') || '6'
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export const load: PageServerLoad = async ({ fetch, params }) => {
|
|||
const resp = await fetch(`/api/articles?page=${page}`);
|
||||
const { articles, currentPage, totalPages, limit, totalArticles }: ArticlePageLoad =
|
||||
await resp.json();
|
||||
|
||||
return {
|
||||
articles,
|
||||
currentPage,
|
||||
|
|
|
|||
Loading…
Reference in a new issue