Skip to content

Commit ebea8c3

Browse files
committed
Related posts
1 parent b4db2fb commit ebea8c3

2 files changed

Lines changed: 151 additions & 1 deletion

File tree

src/routes/posts/+layout.server.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { load as postsLoad } from '../+page.server';
2+
3+
export const prerender = true;
4+
5+
const MIN_POSTS = 5;
6+
7+
export async function load({ url }) {
8+
const postName = (/^\/posts\/([^/]+)$/.exec(url.pathname) || [])[1] || '';
9+
if (!postName) {
10+
return;
11+
}
12+
13+
const posts = await postsLoad();
14+
const shareImages = import.meta.glob('./*/share.png', {
15+
query: 'url'
16+
});
17+
const names = [...Object.keys(shareImages)].map(it => /\/(.+)\//.exec(it)?.[1]);
18+
const images = (await Promise.all([...Object.values(shareImages)].map(it => it()))) as {
19+
default: string;
20+
}[];
21+
const imagesMap: Record<string, string> = {};
22+
images.forEach((it, index) => {
23+
const name = names[index];
24+
if (name) {
25+
imagesMap[name] = it.default;
26+
}
27+
});
28+
29+
const post = posts.posts.find(it => it.name === postName);
30+
if (!post) {
31+
return;
32+
}
33+
34+
const currentTags = new Set(post.tags);
35+
36+
const postsWithTags = posts.posts.filter(it => it.name !== postName).map(post => {
37+
return {
38+
post,
39+
commonTags: currentTags.intersection(new Set(post.tags))
40+
};
41+
});
42+
const sorted = postsWithTags.toSorted((a, b) => {
43+
return b.commonTags.size - a.commonTags.size;
44+
});
45+
const relatedPosts = sorted.slice(0, MIN_POSTS).map(it => {
46+
return {
47+
...it.post,
48+
shareImage: imagesMap[it.post.name]
49+
};
50+
});
51+
52+
return {
53+
post,
54+
relatedPosts
55+
};
56+
}

src/routes/posts/+layout.svelte

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import './toc.css';
44
import './markdown.css';
55
6-
let { children } = $props();
6+
let { children, data } = $props();
77
88
function recalcActiveHeader(): void {
99
const headers = Array.from(
@@ -71,6 +71,16 @@
7171
}
7272
}
7373
74+
// function formatDate(dateStr: string): string {
75+
// const date = new Date(dateStr);
76+
77+
// return [
78+
// String(date.getDate()).padStart(2, '0'),
79+
// String(date.getMonth() + 1).padStart(2, '0'),
80+
// date.getFullYear()
81+
// ].join('.');
82+
// }
83+
7484
onMount(() => {
7585
recalcActiveHeader();
7686
});
@@ -98,6 +108,29 @@
98108
<span class="icon icon_telegram"></span>
99109
Обсудить в Telegram
100110
</a>
111+
112+
{#if data?.relatedPosts?.length}
113+
<h4 class="article__related-title">
114+
Почитать ещё посты
115+
</h4>
116+
117+
<ul class="article__related">
118+
{#each data.relatedPosts as post}
119+
<li class="article__related-post">
120+
<a href="/posts/{post.name}" class="article__related-link">
121+
<img
122+
class="article__related-image"
123+
alt={post.title}
124+
src={post.shareImage}
125+
>
126+
<!-- <div class="article__related-date">
127+
{formatDate(post.date)}
128+
</div> -->
129+
</a>
130+
</li>
131+
{/each}
132+
</ul>
133+
{/if}
101134
</div>
102135

103136
<style>
@@ -178,4 +211,65 @@
178211
.icon_telegram {
179212
mask-image: url(./telegram.svg);
180213
}
214+
215+
.article__related-title {
216+
margin-top: 100px;
217+
}
218+
219+
.article__related {
220+
position: relative;
221+
z-index: 1;
222+
display: flex;
223+
flex-direction: row;
224+
gap: 12px;
225+
margin: 0 -20px;
226+
padding: 0 20px 20px;
227+
overflow-x: auto;
228+
list-style: none;
229+
}
230+
231+
@media (--desktop-window) {
232+
.article__related {
233+
--toc-width: 16rem;
234+
235+
margin-right: calc((20px + var(--toc-width)) * -1);
236+
}
237+
}
238+
239+
@media (--desktop-window-large) {
240+
.article__related {
241+
margin-right: 0;
242+
margin-left: 0;
243+
padding-right: 0;
244+
padding-left: 0;
245+
}
246+
}
247+
248+
.article__related-link {
249+
display: block;
250+
padding: 12px;
251+
border-radius: 12px;
252+
color: inherit;
253+
text-decoration: none;
254+
background-color: var(--bg-quaternary);
255+
transition: background-color .15s ease-in-out;
256+
}
257+
258+
.article__related-link:hover {
259+
background-color: var(--bg-tertiary);
260+
}
261+
262+
.article__related-image {
263+
display: block;
264+
width: 200px;
265+
max-width: none;
266+
margin: 0;
267+
aspect-ratio: 1200 / 630;
268+
pointer-events: none;
269+
}
270+
271+
/* .article__related-date {
272+
margin-top: 12px;
273+
text-align: center;
274+
} */
181275
</style>

0 commit comments

Comments
 (0)