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": {
|
"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",
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 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()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue