mirror of
https://github.com/saicaca/fuwari.git
synced 2026-01-11 14:52:52 +01:00
fix: unable to list uncategorized posts on the archive page (fix #471)
This commit is contained in:
@@ -1,118 +0,0 @@
|
|||||||
---
|
|
||||||
import { UNCATEGORIZED } from "@constants/constants";
|
|
||||||
import I18nKey from "../i18n/i18nKey";
|
|
||||||
import { i18n } from "../i18n/translation";
|
|
||||||
import { getSortedPosts } from "../utils/content-utils";
|
|
||||||
import { getPostUrlBySlug } from "../utils/url-utils";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
keyword?: string;
|
|
||||||
tags?: string[];
|
|
||||||
categories?: string[];
|
|
||||||
}
|
|
||||||
const { tags, categories } = Astro.props;
|
|
||||||
|
|
||||||
let posts = await getSortedPosts();
|
|
||||||
|
|
||||||
if (Array.isArray(tags) && tags.length > 0) {
|
|
||||||
posts = posts.filter(
|
|
||||||
(post) =>
|
|
||||||
Array.isArray(post.data.tags) &&
|
|
||||||
post.data.tags.some((tag) => tags.includes(tag)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(categories) && categories.length > 0) {
|
|
||||||
posts = posts.filter(
|
|
||||||
(post) =>
|
|
||||||
(post.data.category && categories.includes(post.data.category)) ||
|
|
||||||
(!post.data.category && categories.includes(UNCATEGORIZED)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const groups: { year: number; posts: typeof posts }[] = (() => {
|
|
||||||
const groupedPosts = posts.reduce(
|
|
||||||
(grouped: { [year: number]: typeof posts }, post) => {
|
|
||||||
const year = post.data.published.getFullYear();
|
|
||||||
if (!grouped[year]) {
|
|
||||||
grouped[year] = [];
|
|
||||||
}
|
|
||||||
grouped[year].push(post);
|
|
||||||
return grouped;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
// convert the object to an array
|
|
||||||
const groupedPostsArray = Object.keys(groupedPosts).map((key) => ({
|
|
||||||
year: Number.parseInt(key),
|
|
||||||
posts: groupedPosts[Number.parseInt(key)],
|
|
||||||
}));
|
|
||||||
|
|
||||||
// sort years by latest first
|
|
||||||
groupedPostsArray.sort((a, b) => b.year - a.year);
|
|
||||||
return groupedPostsArray;
|
|
||||||
})();
|
|
||||||
|
|
||||||
function formatDate(date: Date) {
|
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
|
||||||
const day = date.getDate().toString().padStart(2, "0");
|
|
||||||
return `${month}-${day}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTag(tag: string[]) {
|
|
||||||
return tag.map((t) => `#${t}`).join(" ");
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="card-base px-8 py-6">
|
|
||||||
{
|
|
||||||
groups.map(group => (
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-row w-full items-center h-[3.75rem]">
|
|
||||||
<div class="w-[15%] md:w-[10%] transition text-2xl font-bold text-right text-75">{group.year}</div>
|
|
||||||
<div class="w-[15%] md:w-[10%]">
|
|
||||||
<div class="h-3 w-3 bg-none rounded-full outline outline-[var(--primary)] mx-auto -outline-offset-[2px] z-50 outline-3"></div>
|
|
||||||
</div>
|
|
||||||
<div class="w-[70%] md:w-[80%] transition text-left text-50">{group.posts.length} {i18n(I18nKey.postsCount)}</div>
|
|
||||||
</div>
|
|
||||||
{group.posts.map(post => (
|
|
||||||
<a href={getPostUrlBySlug(post.slug)}
|
|
||||||
aria-label={post.data.title}
|
|
||||||
class="group btn-plain !block h-10 w-full rounded-lg hover:text-[initial]"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row justify-start items-center h-full">
|
|
||||||
<!-- date -->
|
|
||||||
<div class="w-[15%] md:w-[10%] transition text-sm text-right text-50">
|
|
||||||
{formatDate(post.data.published)}
|
|
||||||
</div>
|
|
||||||
<!-- dot and line -->
|
|
||||||
<div class="w-[15%] md:w-[10%] relative dash-line h-full flex items-center">
|
|
||||||
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
|
|
||||||
bg-[oklch(0.5_0.05_var(--hue))] group-hover:bg-[var(--primary)]
|
|
||||||
outline outline-4 z-50
|
|
||||||
outline-[var(--card-bg)]
|
|
||||||
group-hover:outline-[var(--btn-plain-bg-hover)]
|
|
||||||
group-active:outline-[var(--btn-plain-bg-active)]
|
|
||||||
"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<!-- post title -->
|
|
||||||
<div class="w-[70%] md:max-w-[65%] md:w-[65%] text-left font-bold
|
|
||||||
group-hover:translate-x-1 transition-all group-hover:text-[var(--primary)]
|
|
||||||
text-75 pr-8 whitespace-nowrap overflow-ellipsis overflow-hidden"
|
|
||||||
>
|
|
||||||
{post.data.title}
|
|
||||||
</div>
|
|
||||||
<!-- tag list -->
|
|
||||||
<div class="hidden md:block md:w-[15%] text-left text-sm transition
|
|
||||||
whitespace-nowrap overflow-ellipsis overflow-hidden
|
|
||||||
text-30"
|
|
||||||
>{formatTag(post.data.tags)}</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
import { UNCATEGORIZED } from "@constants/constants";
|
|
||||||
import I18nKey from "../i18n/i18nKey";
|
import I18nKey from "../i18n/i18nKey";
|
||||||
import { i18n } from "../i18n/translation";
|
import { i18n } from "../i18n/translation";
|
||||||
import { getPostUrlBySlug } from "../utils/url-utils";
|
import { getPostUrlBySlug } from "../utils/url-utils";
|
||||||
@@ -13,6 +12,7 @@ export let sortedPosts: Post[] = [];
|
|||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
tags = params.has("tag") ? params.getAll("tag") : [];
|
tags = params.has("tag") ? params.getAll("tag") : [];
|
||||||
categories = params.has("category") ? params.getAll("category") : [];
|
categories = params.has("category") ? params.getAll("category") : [];
|
||||||
|
const uncategorized = params.get("uncategorized");
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -54,12 +54,14 @@ onMount(async () => {
|
|||||||
|
|
||||||
if (categories.length > 0) {
|
if (categories.length > 0) {
|
||||||
filteredPosts = filteredPosts.filter(
|
filteredPosts = filteredPosts.filter(
|
||||||
(post) =>
|
(post) => post.data.category && categories.includes(post.data.category),
|
||||||
(post.data.category && categories.includes(post.data.category)) ||
|
|
||||||
(!post.data.category && categories.includes(UNCATEGORIZED)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uncategorized) {
|
||||||
|
filteredPosts = filteredPosts.filter((post) => !post.data.category);
|
||||||
|
}
|
||||||
|
|
||||||
const grouped = filteredPosts.reduce(
|
const grouped = filteredPosts.reduce(
|
||||||
(acc, post) => {
|
(acc, post) => {
|
||||||
const year = post.data.published.getFullYear();
|
const year = post.data.published.getFullYear();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import WidgetLayout from "./WidgetLayout.astro";
|
|||||||
import I18nKey from "../../i18n/i18nKey";
|
import I18nKey from "../../i18n/i18nKey";
|
||||||
import { i18n } from "../../i18n/translation";
|
import { i18n } from "../../i18n/translation";
|
||||||
import { getCategoryList } from "../../utils/content-utils";
|
import { getCategoryList } from "../../utils/content-utils";
|
||||||
import { getCategoryUrl } from "../../utils/url-utils";
|
|
||||||
import ButtonLink from "../control/ButtonLink.astro";
|
import ButtonLink from "../control/ButtonLink.astro";
|
||||||
|
|
||||||
const categories = await getCategoryList();
|
const categories = await getCategoryList();
|
||||||
@@ -27,7 +26,7 @@ const style = Astro.props.style;
|
|||||||
>
|
>
|
||||||
{categories.map((c) =>
|
{categories.map((c) =>
|
||||||
<ButtonLink
|
<ButtonLink
|
||||||
url={getCategoryUrl(c.name)}
|
url={c.url}
|
||||||
badge={String(c.count)}
|
badge={String(c.count)}
|
||||||
label={`View all posts in the ${c.name.trim()} category`}
|
label={`View all posts in the ${c.name.trim()} category`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
export const UNCATEGORIZED = "__uncategorized__";
|
|
||||||
|
|
||||||
export const PAGE_SIZE = 8;
|
export const PAGE_SIZE = 8;
|
||||||
|
|
||||||
export const LIGHT_MODE = "light",
|
export const LIGHT_MODE = "light",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const postsCollection = defineCollection({
|
|||||||
description: z.string().optional().default(""),
|
description: z.string().optional().default(""),
|
||||||
image: z.string().optional().default(""),
|
image: z.string().optional().default(""),
|
||||||
tags: z.array(z.string()).optional().default([]),
|
tags: z.array(z.string()).optional().default([]),
|
||||||
category: z.string().optional().default(""),
|
category: z.string().optional().nullable().default(""),
|
||||||
lang: z.string().optional().default(""),
|
lang: z.string().optional().default(""),
|
||||||
|
|
||||||
/* For internal use */
|
/* For internal use */
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import I18nKey from "@i18n/i18nKey";
|
import I18nKey from "@i18n/i18nKey";
|
||||||
import { i18n } from "@i18n/translation";
|
import { i18n } from "@i18n/translation";
|
||||||
|
import { getCategoryUrl } from "@utils/url-utils.ts";
|
||||||
|
|
||||||
export async function getSortedPosts() {
|
export async function getSortedPosts() {
|
||||||
const allBlogPosts = await getCollection("posts", ({ data }) => {
|
const allBlogPosts = await getCollection("posts", ({ data }) => {
|
||||||
@@ -54,6 +55,7 @@ export async function getTagList(): Promise<Tag[]> {
|
|||||||
export type Category = {
|
export type Category = {
|
||||||
name: string;
|
name: string;
|
||||||
count: number;
|
count: number;
|
||||||
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getCategoryList(): Promise<Category[]> {
|
export async function getCategoryList(): Promise<Category[]> {
|
||||||
@@ -61,7 +63,7 @@ export async function getCategoryList(): Promise<Category[]> {
|
|||||||
return import.meta.env.PROD ? data.draft !== true : true;
|
return import.meta.env.PROD ? data.draft !== true : true;
|
||||||
});
|
});
|
||||||
const count: { [key: string]: number } = {};
|
const count: { [key: string]: number } = {};
|
||||||
allBlogPosts.map((post: { data: { category: string | number } }) => {
|
allBlogPosts.map((post: { data: { category: string | null } }) => {
|
||||||
if (!post.data.category) {
|
if (!post.data.category) {
|
||||||
const ucKey = i18n(I18nKey.uncategorized);
|
const ucKey = i18n(I18nKey.uncategorized);
|
||||||
count[ucKey] = count[ucKey] ? count[ucKey] + 1 : 1;
|
count[ucKey] = count[ucKey] ? count[ucKey] + 1 : 1;
|
||||||
@@ -82,7 +84,11 @@ export async function getCategoryList(): Promise<Category[]> {
|
|||||||
|
|
||||||
const ret: Category[] = [];
|
const ret: Category[] = [];
|
||||||
for (const c of lst) {
|
for (const c of lst) {
|
||||||
ret.push({ name: c, count: count[c] });
|
ret.push({
|
||||||
|
name: c,
|
||||||
|
count: count[c],
|
||||||
|
url: getCategoryUrl(c),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function getCategoryUrl(category: string): string {
|
|||||||
category.trim() === "" ||
|
category.trim() === "" ||
|
||||||
category.trim().toLowerCase() === i18n(I18nKey.uncategorized).toLowerCase()
|
category.trim().toLowerCase() === i18n(I18nKey.uncategorized).toLowerCase()
|
||||||
)
|
)
|
||||||
return url("/archive/");
|
return url("/archive/?uncategorized=true");
|
||||||
return url(`/archive/?category=${encodeURIComponent(category.trim())}`);
|
return url(`/archive/?category=${encodeURIComponent(category.trim())}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user