Compare commits
22 Commits
3378844823
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 582051ba20 | |||
| a25500bb91 | |||
| c3e05b44ea | |||
| b4f8a42e05 | |||
| ff486c01e3 | |||
| 33ead9d968 | |||
| 20f8b38c8a | |||
| 7f0b65fa20 | |||
| 2fdaf64b47 | |||
| 4bad9e095e | |||
| facb2b0f99 | |||
| ef0c8dd981 | |||
| 32b9a29028 | |||
| 1da77d6074 | |||
| 64ecc338be | |||
| 66623a64b4 | |||
| 6997e313e6 | |||
| 435ca0f2d7 | |||
| 3e742ba952 | |||
| caad696191 | |||
| 51419e4c7c | |||
| 4310c90f48 |
18
web/src/components/albums/albumCard.astro
Normal file
18
web/src/components/albums/albumCard.astro
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
import { authPB } from "@utils/pocketbase"
|
||||
const { a } = Astro.props
|
||||
const pb = await authPB()
|
||||
---
|
||||
|
||||
<div class="swiper-slide flex flex-col md:flex-row items-center justify-center">
|
||||
<a class="b1-no-underline" href={`/albums/${encodeURI(a.title)}`}>
|
||||
<h1 class="text-center pb-0 md:flex-1">{a.title}</h1>
|
||||
<p class="text-center">{a.date}</p>
|
||||
<img
|
||||
src={pb.files.getURL(a, a.images[0])}
|
||||
class="w-full h-full object-contain"
|
||||
loading="lazy"
|
||||
alt={a.title}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
@@ -12,10 +12,14 @@ const links = [
|
||||
txt: "email",
|
||||
lnk: "mailto:contact@breadone.net"
|
||||
},
|
||||
{
|
||||
txt: "copyright",
|
||||
lnk: "/copyright"
|
||||
}
|
||||
// {
|
||||
// txt: "about",
|
||||
// lnk: "/about"
|
||||
// },
|
||||
// {
|
||||
// txt: "copyright",
|
||||
// lnk: "/copyright"
|
||||
// }
|
||||
]
|
||||
---
|
||||
|
||||
|
||||
@@ -4,9 +4,13 @@ const links = [
|
||||
txt: 'now',
|
||||
lnk: '/now'
|
||||
},
|
||||
// {
|
||||
// txt: 'photos',
|
||||
// lnk: '/photos'
|
||||
// },
|
||||
{
|
||||
txt: 'photos',
|
||||
lnk: '/photos'
|
||||
txt: 'albums',
|
||||
lnk: '/albums'
|
||||
},
|
||||
{
|
||||
txt: 'posts',
|
||||
|
||||
36
web/src/components/index/recentAlbums.astro
Normal file
36
web/src/components/index/recentAlbums.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import SummaryCard from "./summaryCard.astro";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import { getFormattedDate } from "@utils/date";
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const albums = await pb.collection('albums').getList(1, 3, {
|
||||
sort: '-created',
|
||||
})
|
||||
|
||||
const postImages = await Promise.all( albums.items.map(a => pb.files.getURL(a, a.images[0])) )
|
||||
|
||||
---
|
||||
<SummaryCard
|
||||
title="Recent Albums",
|
||||
titleLink="/albums"
|
||||
>
|
||||
<div class="flex flex-col md:flex-row gap-4 md:gap-4">
|
||||
{
|
||||
albums.items.map((p, i) => (
|
||||
<div class="flex md:flex-col flex-row gap-3 md:gap-2 flex-1">
|
||||
<a href={`/albums/${encodeURI(p.title)}`} class="shrink-0">
|
||||
<img class="w-24 h-24 md:w-full md:h-48 object-cover rounded-sm" src={postImages[i]} alt={p.title}/>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col justify-start md:justify-start">
|
||||
<a href={`/albums/${encodeURI(p.title)}`} class="b1-no-underline line-clamp-2 md:line-clamp-none">
|
||||
{p.title}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</SummaryCard>
|
||||
@@ -23,7 +23,7 @@ const postImages = await Promise.all( posts.items.map(p => pb.files.getURL(p, p.
|
||||
<div class="flex md:flex-col flex-row gap-3 md:gap-2 flex-1">
|
||||
<time class="hidden md:block text-sm text-gray-500" datetime={p.publishDate}>{getFormattedDate(p.publishDate)}</time>
|
||||
<a href={`/posts/${p.slug}`} class="shrink-0">
|
||||
<img class="w-24 h-24 md:w-full md:h-48 object-cover rounded-lg" src={postImages[i]} alt={p.title}/>
|
||||
<img class="w-24 h-24 md:w-full md:h-48 object-cover rounded-sm" src={postImages[i]} alt={p.title}/>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col justify-start md:justify-start">
|
||||
|
||||
@@ -5,11 +5,12 @@ import { authPB } from "src/utils/pocketbase";
|
||||
|
||||
const pb = await authPB()
|
||||
const photos = await pb.collection('photos').getFullList({
|
||||
sort: '-created'
|
||||
sort: '-created',
|
||||
filter: 'published=true'
|
||||
})
|
||||
|
||||
const photoLinks = await Promise.all(
|
||||
photos.map(p => pb.files.getURL(p, p.image))
|
||||
photos.map(p => pb.files.getURL(p, p.image, { thumb: '0x1800' }))
|
||||
)
|
||||
---
|
||||
|
||||
@@ -25,6 +26,7 @@ const photoLinks = await Promise.all(
|
||||
const photoLinks = photoLinksElement ? JSON.parse(photoLinksElement.textContent || '[]') : [];
|
||||
const titleEl = document.getElementById('photo-title');
|
||||
const cameraEl = document.getElementById('photo-camera');
|
||||
const filmEl = document.getElementById('photo-film');
|
||||
const locationEl = document.getElementById('photo-location');
|
||||
const currentPosIndicator = document.getElementById('current-pos');
|
||||
const maxPosIndicator = document.getElementById('max-pos');
|
||||
@@ -36,18 +38,10 @@ const photoLinks = await Promise.all(
|
||||
currentPosIndicator.innerText = '1';
|
||||
}
|
||||
|
||||
function preloadImages() {
|
||||
photoLinks.forEach((url: string) => {
|
||||
const preloadImg = new Image();
|
||||
preloadImg.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
// Call preload on page load
|
||||
preloadImages();
|
||||
|
||||
const swiper = new Swiper('.swiper', {
|
||||
modules: [Navigation, Keyboard, EffectFade],
|
||||
spaceBetween: 20,
|
||||
effect: 'cards',
|
||||
fadeEffect: {
|
||||
crossFade: true
|
||||
@@ -77,6 +71,9 @@ const photoLinks = await Promise.all(
|
||||
if (cameraEl && currentPhoto) {
|
||||
cameraEl.textContent = `📸 ${currentPhoto.camera}`;
|
||||
}
|
||||
if (filmEl && currentPhoto) {
|
||||
filmEl.textContent = currentPhoto.film !== "" ? `🎞️ ${currentPhoto.film}` : "";
|
||||
}
|
||||
if (locationEl && currentPhoto) {
|
||||
locationEl.textContent = `📌 ${currentPhoto.location}`;
|
||||
}
|
||||
@@ -96,6 +93,7 @@ const photoLinks = await Promise.all(
|
||||
<img
|
||||
src={photoLinks[i]}
|
||||
class="w-full h-full object-contain"
|
||||
loading="lazy"
|
||||
alt={photo.title || 'Photo'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,16 @@ import { getFormattedDate } from '@utils/date'
|
||||
const { publishDate, wordCount, readTime, tags } = Astro.props
|
||||
---
|
||||
|
||||
<time datetime={publishDate}>{getFormattedDate(publishDate)}</time> |
|
||||
<span title={`${wordCount} words`}>{readTime} min. read</span> |
|
||||
{
|
||||
<div class="flex flex-row gap-1">
|
||||
<time datetime={publishDate}>{getFormattedDate(publishDate)}</time>
|
||||
|
||||
<span class="tag-separator"/>
|
||||
|
||||
<span title={`${wordCount} words`}>{readTime} min. read</span>
|
||||
|
||||
<span class="tag-separator"/>
|
||||
|
||||
{
|
||||
tags.map((tag, i) => (
|
||||
<>
|
||||
<a class="b1-no-underline" href={`/posts/tag/${tag}/`}>
|
||||
@@ -14,4 +21,5 @@ const { publishDate, wordCount, readTime, tags } = Astro.props
|
||||
{i < tags.length - 1 && ", "}
|
||||
</>
|
||||
))
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -38,14 +38,11 @@
|
||||
}
|
||||
|
||||
// get all titles in the page
|
||||
const titles = document.querySelectorAll('h2, h3')
|
||||
const titles = document.querySelectorAll('h1, h2, h3')
|
||||
const toc = document.getElementById('toc') as Element
|
||||
const toc_cnt = document.getElementById('toc-container') as Element
|
||||
|
||||
console.log(titles)
|
||||
|
||||
// if there's more than two titles (ie, there's more than just the article title and TOC title, then appropriate to show)
|
||||
if (titles.length > 2) {
|
||||
if (titles.length > 0) {
|
||||
toc_cnt.classList.add('lg:block')
|
||||
}
|
||||
|
||||
@@ -54,19 +51,7 @@
|
||||
|
||||
// Assign IDs in case they haven't been (by me)
|
||||
t.id = t.innerHTML
|
||||
|
||||
let depth = 1
|
||||
|
||||
// determine what depth to use
|
||||
switch (t.tagName) {
|
||||
case 'H2':
|
||||
depth = 1
|
||||
break
|
||||
case 'H3':
|
||||
depth = 2
|
||||
break
|
||||
}
|
||||
|
||||
let depth = t.tagName.substring(1) as number
|
||||
addLink(t, depth)
|
||||
}
|
||||
</script>
|
||||
@@ -22,7 +22,7 @@ const readTime = calcReadTime(wordCount);
|
||||
<p class="line-clamp-3 block italic text-gray-600">{p.description}</p>
|
||||
}
|
||||
|
||||
<div class="flex flex-row gap-1">
|
||||
|
||||
<PostInfo
|
||||
publishDate={p.publishDate},
|
||||
wordCount={wordCount},
|
||||
@@ -30,7 +30,6 @@ const readTime = calcReadTime(wordCount);
|
||||
tags={p.tags}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,27 @@ import BaseHead from "./BaseHead.astro"
|
||||
import Sidebar from "@components/sidebar"
|
||||
import Navbar from "@components/index/navbar"
|
||||
import Footer from "@components/footer"
|
||||
|
||||
const {
|
||||
meta = {}
|
||||
} = Astro.props;
|
||||
|
||||
const {
|
||||
title = "",
|
||||
description = "",
|
||||
ogImage = "",
|
||||
articleDate = ""
|
||||
} = meta;
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<BaseHead/>
|
||||
<BaseHead
|
||||
title={title}
|
||||
description={description}
|
||||
ogImage={ogImage}
|
||||
articleDate={articleDate}
|
||||
/>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
27
web/src/pages/albums/[title].astro
Normal file
27
web/src/pages/albums/[title].astro
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
export const prerender = false
|
||||
|
||||
const { title } = Astro.params
|
||||
const pb = await authPB()
|
||||
const album = await pb.collection('albums').getFirstListItem(`title ~ "${decodeURI(title!)}"`)
|
||||
const images = await Promise.all(
|
||||
album.images.map(i => pb.files.getURL(album, i))
|
||||
)
|
||||
---
|
||||
|
||||
<Base meta={{title: "album", description: album.title, ogImage: images[0]}}>
|
||||
|
||||
<div slot="sidebar">
|
||||
<h1>{album.title}</h1>
|
||||
<p>{album.date}</p>
|
||||
<p>{album.location}</p><br>
|
||||
|
||||
<Fragment set:html={album.remarks}/>
|
||||
</div>
|
||||
|
||||
<div slot="content">
|
||||
{ images.map(img => <img class="pb-2" src={img}/> ) }
|
||||
</div>
|
||||
</Base>
|
||||
49
web/src/pages/albums/index.astro
Normal file
49
web/src/pages/albums/index.astro
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import AlbumCard from "@components/albums/albumCard";
|
||||
export const prerender = false
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const albums = await pb.collection('albums').getFullList({
|
||||
sort: '-created'
|
||||
})
|
||||
---
|
||||
|
||||
<script>
|
||||
import Swiper from "swiper";
|
||||
import { Keyboard } from 'swiper/modules';
|
||||
import 'swiper/css';
|
||||
|
||||
const s = new Swiper('.swiper-album', {
|
||||
modules: [Keyboard],
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 0,
|
||||
speed: 400,
|
||||
loop: true,
|
||||
keyboard: {
|
||||
enabled: true,
|
||||
onlyInViewport: false,
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
<Base meta={{title: "albums"}}>
|
||||
<div slot="sidebar">
|
||||
<p class="text-left"> More focused, themed collections of photos compared to the more miscellaneous images of the (now defunct) <a href="/photos">photos page.</a> Best viewed on a large screen!</p>
|
||||
<!-- <br>
|
||||
← / → -->
|
||||
</div>
|
||||
|
||||
<div slot="content">
|
||||
<div class=" w-full">
|
||||
<div class="">
|
||||
{ albums.map(a => <AlbumCard a={a}/> ) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Base>
|
||||
@@ -3,7 +3,7 @@ import Base from "src/layout/Base.astro"
|
||||
import Sidebar from "src/components/sidebar.astro"
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "copyright"}}>
|
||||
<div slot="content">
|
||||
<p class="text-xl">Copyright & License</p>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import Base from "src/layout/Base.astro"
|
||||
import RecentPosts from "@components/index/recentPosts"
|
||||
import RecentAlbums from "@components/index/recentAlbums"
|
||||
|
||||
export const prerender = false
|
||||
|
||||
@@ -9,13 +10,16 @@ export const prerender = false
|
||||
<Base>
|
||||
<div slot="sidebar" class="text-left">
|
||||
<p>New website new me!</p> <br>
|
||||
<p>Check out some of the photos and posts I've added recently, as well as the new design.</p><br>
|
||||
|
||||
<!-- todo: write this post lmfao -->
|
||||
<p>If you're interested, I actually wrote a post about the new design and all that went into it.</p>
|
||||
<p>This is the third major iteration of the website, and by far the most flexible and best designed (imo!)</p><br>
|
||||
|
||||
<p>I'm... really not sure what else to put on this landing page, make sure to check out some of the photos and posts I've added recently, I've been totally revamping those too and I'm really proud of this batch.</p><br>
|
||||
|
||||
<p>Hope you enjoy your time here :)</p>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="md:py-10">
|
||||
<div slot="content" class="md:py-10 space-y-4">
|
||||
<RecentAlbums/>
|
||||
<RecentPosts/>
|
||||
</div>
|
||||
</Base>
|
||||
|
||||
37
web/src/pages/now.astro
Normal file
37
web/src/pages/now.astro
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import Base from "@layout/Base";
|
||||
import { authPB } from "@utils/pocketbase";
|
||||
import { getFormattedDate } from '@utils/date'
|
||||
export const prerender = false
|
||||
|
||||
const pb = await authPB()
|
||||
|
||||
const records = await pb.collection('now').getFullList({
|
||||
sort: '-updated'
|
||||
})
|
||||
|
||||
const lastUpdated = getFormattedDate(records[0].updated)
|
||||
---
|
||||
|
||||
<Base meta={{title: "now"}}>
|
||||
|
||||
<div slot="sidebar" class="text-left">
|
||||
<h2>The Now!</h2>
|
||||
<p>Trying out a <a href="https://nownownow.com/about">Now Page</a>, inspired by some other personal sites such as <a href="https://lai.nz">this great one.</a></p><br>
|
||||
|
||||
<p>I'll update this every now and then; the gist is "this is what you would tell someone that you haven't seem in a year, what you've been up to". Love that concept!</p>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="">
|
||||
<p class="mb-1 md:mt-5 italic ">Last Updated: {lastUpdated}</p>
|
||||
{
|
||||
records.map(r => (
|
||||
<div class="mb-3">
|
||||
<h3>{r.heading}</h3>
|
||||
<Fragment set:html={r.content}/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
</Base>
|
||||
@@ -11,15 +11,16 @@ const photos = await pb.collection('photos').getFullList({
|
||||
})
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "photos"}}>
|
||||
<div slot="sidebar">
|
||||
<p id="photo-title" class="bold text-2xl">{photos[0].title === "" ? "Untitled" : photos[0].title}</p>
|
||||
<p id="photo-camera" class="text-sm">📸 {photos[0].camera}</p>
|
||||
<p id="photo-film" class="text-sm">{photos[0].film !== "" ? `🎞️ ${photos[0].film}` : ""}</p>
|
||||
<p id="photo-location" class="text-sm">📌 {photos[0].location}</p>
|
||||
|
||||
<!-- <button id="dec-button" onclick="dec()"><</button> -->
|
||||
<button id="dec-button" onclick="dec()"><</button>
|
||||
<span id="current-pos" /> of <span id="max-pos" />
|
||||
<!-- <button id="inc-button" onclick="inc()">></button> -->
|
||||
<button id="inc-button" onclick="inc()">></button>
|
||||
</div>
|
||||
|
||||
<div slot="content" class="">
|
||||
|
||||
@@ -20,7 +20,7 @@ const readTime = calcReadTime(wordCount);
|
||||
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "post", description: title, ogImage: headerImage, articleDate: publishDate}}>
|
||||
<div slot="sidebar" class="text-left">
|
||||
<img src={headerImage}/>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ const tags = posts.flatMap(p => p.tags)
|
||||
const uniqueTags = tags.filter((v, i, a) => a.indexOf(v) === i)
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Base meta={{title: "posts"}}>
|
||||
<div slot="sidebar" class="text-left">
|
||||
A few thoughts I've had, whenever I feel like it as you can see!
|
||||
|
||||
|
||||
@@ -42,9 +42,22 @@
|
||||
@apply hover:decoration-(--accent);
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-xl;
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
@apply py-4;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-separator {
|
||||
@apply border-l border-gray-400;
|
||||
}
|
||||
|
||||
.content-panel {
|
||||
|
||||
Reference in New Issue
Block a user