-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #53 from OUCC/miyaji/blog-statistics
タグ一覧と関連記事を追加
- Loading branch information
Showing
15 changed files
with
292 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
import type { CollectionEntry } from 'astro:content' | ||
import Section from '@/components/common/Section.astro' | ||
import BlogList from '@/components/blog/BlogList.astro' | ||
interface Props { | ||
blogs: CollectionEntry<'blogs'>[] | ||
} | ||
const { blogs } = Astro.props | ||
--- | ||
|
||
<Section background="secondary"> | ||
<Fragment slot="title">関連記事</Fragment> | ||
<BlogList blogs={blogs} sorted /> | ||
</Section> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,22 @@ | ||
--- | ||
import type { CollectionEntry } from 'astro:content' | ||
import TagIcon from '../icon/TagIcon.astro' | ||
import TagListItemCard from './TagListItemCard.astro' | ||
interface Props { | ||
tags: CollectionEntry<'tags'>[] | ||
tags: (CollectionEntry<'tags'> & { articleCount: number })[] | ||
} | ||
const { tags } = Astro.props | ||
--- | ||
|
||
<ul class="flex flex-wrap gap-2"> | ||
<ul class="flex sm:flex-row flex-col flex-wrap gap-3"> | ||
{ | ||
tags.map((tag) => ( | ||
<li> | ||
<a | ||
href={`/blog/tags/${tag.id}`} | ||
class="flex items-center gap-1 bg-gray-100 hover:bg-gray-200 text-sm text-justify p-1 rounded-full" | ||
> | ||
<TagIcon tagImage={tag.data.image} size={18} /> | ||
<p>{tag.data.name}</p> | ||
</a> | ||
</li> | ||
<TagListItemCard | ||
{...tag.data} | ||
id={tag.id} | ||
articleCount={tag.articleCount} | ||
/> | ||
)) | ||
} | ||
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
--- | ||
import type { CollectionEntry } from 'astro:content' | ||
import TagIcon from '../icon/TagIcon.astro' | ||
type Props = CollectionEntry<'tags'>['data'] & { | ||
id: CollectionEntry<'tags'>['id'] | ||
articleCount: number | ||
} | ||
const { id, articleCount, ...tag } = Astro.props | ||
--- | ||
|
||
<li | ||
class="block sm:basis-[calc((100%-0.75rem)/2)] bg-white rounded-xl border space-y-1 group" | ||
> | ||
<a class="w-full h-full p-4 flex items-center" href={`/blog/tags/${id}`}> | ||
<TagIcon tagImage={tag.image} size={40} /> | ||
<div class="px-2"> | ||
<h2 class="text-xl font-bold line-clamp-1 group-hover:underline"> | ||
{tag.name} | ||
</h2> | ||
<p class="text-sm text-gray-600">記事数 : {articleCount}</p> | ||
</div> | ||
</a> | ||
</li> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
--- | ||
import type { CollectionEntry } from 'astro:content' | ||
import Section from '@/components/common/Section.astro' | ||
import TagList from './TagList.astro' | ||
interface Props { | ||
title: string | ||
tags: (CollectionEntry<'tags'> & { articleCount: number })[] | ||
} | ||
const { title, tags } = Astro.props | ||
--- | ||
|
||
<Section background="secondary"> | ||
<Fragment slot="title">{title}</Fragment> | ||
<TagList tags={tags} /> | ||
</Section> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
import type { CollectionEntry } from 'astro:content' | ||
import TagIcon from '../icon/TagIcon.astro' | ||
interface Props { | ||
tags: CollectionEntry<'tags'>[] | ||
} | ||
const { tags } = Astro.props | ||
--- | ||
|
||
<ul class="flex flex-wrap gap-2"> | ||
{ | ||
tags.map((tag) => ( | ||
<li> | ||
<a | ||
href={`/blog/tags/${tag.id}`} | ||
class="flex items-center gap-1 bg-gray-100 hover:bg-gray-200 text-sm text-justify p-1 rounded-full" | ||
> | ||
<TagIcon tagImage={tag.data.image} size={18} /> | ||
<p>{tag.data.name}</p> | ||
</a> | ||
</li> | ||
)) | ||
} | ||
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--- | ||
const current = Astro.url.pathname | ||
const navList: { target: string; title: string }[] = [ | ||
{ | ||
target: '/blog', | ||
title: '記事一覧', | ||
}, | ||
{ | ||
target: '/blog/tags', | ||
title: 'タグ一覧', | ||
}, | ||
] | ||
--- | ||
|
||
<div class="flex text-lg px-9 pt-1 gap-3 flex-nowrap"> | ||
{ | ||
navList.map(({ target, title }) => ( | ||
<a | ||
href={target} | ||
class:list={[ | ||
'text-gray-700 hover:text-black whitespace-nowrap', | ||
{ | ||
'after:block after:bg-primary after:h-1 after:rounded-t-lg': | ||
current === target, | ||
'pb-1': current !== target, | ||
}, | ||
]} | ||
> | ||
{title} | ||
</a> | ||
)) | ||
} | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { unreachable } from '@/utils/unreachable' | ||
import { type CollectionEntry, getCollection, getEntry } from 'astro:content' | ||
|
||
type TagId = CollectionEntry<'tags'>['id'] | ||
type BlogSlug = CollectionEntry<'blogs'>['slug'] | ||
export type TagStatistics = Readonly< | ||
Record< | ||
TagId, | ||
readonly Readonly<{ slug: BlogSlug; time: number; timeScore: number }>[] | ||
> | ||
> | ||
export type BlogStatistics = Readonly< | ||
Record< | ||
BlogSlug, | ||
readonly Readonly<{ collection: 'blogs'; slug: BlogSlug; score: number }>[] | ||
> | ||
> | ||
|
||
let tagStatistics: TagStatistics | null = null | ||
let blogStatistics: BlogStatistics | null = null | ||
|
||
function calcTimeScore(milliseconds: number): number { | ||
const diff = Date.now() - milliseconds | ||
|
||
if (diff <= 365 * 24 * 3600_000) return 3 | ||
else if (diff <= 2 * 365 * 24 * 3600_000) return 2 | ||
else return 1 | ||
} | ||
|
||
async function analyze() { | ||
if (blogStatistics === null || tagStatistics === null) { | ||
const blogs = await getCollection('blogs') | ||
|
||
const blogMetas = await Promise.all( | ||
blogs.map((b) => getEntry({ collection: 'blog-metas', id: b.slug })), | ||
) | ||
|
||
const blogWithMeta = blogs | ||
.map((b, i) => ({ | ||
slug: b.slug, | ||
tags: b.data.tags, | ||
time: | ||
blogMetas[i]?.data.updateDate?.getTime() ?? | ||
blogMetas[i]?.data.postDate.getTime() ?? | ||
Date.now(), | ||
timeScore: calcTimeScore( | ||
blogMetas[i]?.data.updateDate?.getTime() ?? | ||
blogMetas[i]?.data.postDate.getTime() ?? | ||
Date.now(), | ||
), | ||
})) | ||
.sort((blog1, blog2) => blog2.time - blog1.time) | ||
const tags = await getCollection('tags') | ||
tagStatistics = Object.fromEntries( | ||
tags.map( | ||
(tag) => | ||
[ | ||
tag.id, | ||
blogWithMeta | ||
.filter((b) => b.tags.some((t) => t.id === tag.id)) | ||
.map(({ tags: _, ...b }) => b) as TagStatistics[TagId], | ||
] as const, | ||
), | ||
) as TagStatistics | ||
|
||
blogStatistics = Object.fromEntries( | ||
blogs.map((blog) => { | ||
const result: { [K in BlogSlug]?: { score: number; time: number } } = {} | ||
blog.data.tags | ||
.flatMap((t) => tagStatistics![t.id]) | ||
.forEach(({ slug, timeScore, time }) => { | ||
if (slug !== blog.slug) { | ||
result[slug] ??= { score: 0, time } | ||
result[slug]!.score += timeScore | ||
} | ||
}) | ||
const resultArr = Object.entries(result) | ||
.sort( | ||
( | ||
[_, { score: score1, time: time1 }], | ||
[__, { score: score2, time: time2 }], | ||
) => (score1 !== score2 ? score2 - score1 : time2 - time1), | ||
) | ||
.map(([slug, { score }]) => ({ | ||
slug, | ||
score, | ||
collection: 'blogs', | ||
})) as BlogStatistics[BlogSlug] | ||
|
||
return [blog.slug, resultArr] as const | ||
}), | ||
) as BlogStatistics | ||
} | ||
} | ||
|
||
export async function getBlogStatistics(slug: BlogSlug) { | ||
await analyze() | ||
if (tagStatistics === null || blogStatistics === null) return unreachable() | ||
|
||
return blogStatistics[slug] | ||
} | ||
|
||
export async function getTagStatistics(): Promise<TagStatistics> | ||
export async function getTagStatistics(id: TagId): Promise<TagStatistics[TagId]> | ||
export async function getTagStatistics(id?: TagId) { | ||
await analyze() | ||
if (tagStatistics === null || blogStatistics === null) return unreachable() | ||
|
||
return id === undefined ? tagStatistics : tagStatistics[id] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.