diff --git a/src/components/blog/TagSelector.tsx b/src/components/blog/TagSelector.tsx
index c649865..a3b88cd 100644
--- a/src/components/blog/TagSelector.tsx
+++ b/src/components/blog/TagSelector.tsx
@@ -1,4 +1,11 @@
-import { createSignal, createEffect, For, Show, onCleanup } from "solid-js";
+import {
+ createSignal,
+ createEffect,
+ createMemo,
+ For,
+ Show,
+ onCleanup
+} from "solid-js";
import { useNavigate, useLocation, useSearchParams } from "@solidjs/router";
export interface TagSelectorProps {
@@ -7,6 +14,7 @@ export interface TagSelectorProps {
export default function TagSelector(props: TagSelectorProps) {
const [showingMenu, setShowingMenu] = createSignal(false);
+ const [showingRareTags, setShowingRareTags] = createSignal(false);
let buttonRef: HTMLButtonElement | undefined;
let menuRef: HTMLDivElement | undefined;
const navigate = useNavigate();
@@ -16,6 +24,22 @@ export default function TagSelector(props: TagSelectorProps) {
const currentSort = () => searchParams.sort || "";
const currentFilters = () => searchParams.filter?.split("|") || [];
+ const frequentTags = createMemo(() =>
+ Object.entries(props.tagMap).filter(([_, count]) => count > 1)
+ );
+
+ const rareTags = createMemo(() =>
+ Object.entries(props.tagMap).filter(([_, count]) => count <= 1)
+ );
+
+ const allTagKeys = createMemo(() =>
+ Object.keys(props.tagMap).map((key) => key.slice(1))
+ );
+
+ const allChecked = createMemo(() =>
+ allTagKeys().every((tag) => !currentFilters().includes(tag))
+ );
+
const handleClickOutside = (e: MouseEvent) => {
if (
buttonRef &&
@@ -65,13 +89,15 @@ export default function TagSelector(props: TagSelectorProps) {
}
};
- const handleUncheckAll = () => {
- // Build filter string with all tags
- const allTags =
- Object.keys(props.tagMap)
- .map((key) => key.slice(1))
- .join("|") + "|";
- navigate(`${location.pathname}?sort=${currentSort()}&filter=${allTags}`);
+ const handleToggleAll = () => {
+ if (allChecked()) {
+ // Uncheck all: Build filter string with all tags
+ const allTags = allTagKeys().join("|") + "|";
+ navigate(`${location.pathname}?sort=${currentSort()}&filter=${allTags}`);
+ } else {
+ // Check all: Remove filter param
+ navigate(`${location.pathname}?sort=${currentSort()}`);
+ }
};
return (
@@ -92,13 +118,13 @@ export default function TagSelector(props: TagSelectorProps) {
-
+
{([key, value]) => (
)}
+
0}>
+
+
+
+
+ {([key, value]) => (
+
+
+ handleCheck(key.slice(1), e.currentTarget.checked)
+ }
+ />
+
+ {`${key.slice(1)} (${value}) `}
+
+
+ )}
+
+
+
+
diff --git a/src/components/icons/Fire.tsx b/src/components/icons/Fire.tsx
new file mode 100644
index 0000000..a82efec
--- /dev/null
+++ b/src/components/icons/Fire.tsx
@@ -0,0 +1,19 @@
+export const Fire = ({
+ color = "#ea580c",
+ height = 120,
+ width = 120,
+ secondaryOpacity = 0.3,
+ ...props
+}) => (
+
+);
diff --git a/src/routes/blog/[title]/index.tsx b/src/routes/blog/[title]/index.tsx
index c612f95..0f1b560 100644
--- a/src/routes/blog/[title]/index.tsx
+++ b/src/routes/blog/[title]/index.tsx
@@ -1,4 +1,4 @@
-import { Show, For } from "solid-js";
+import { Show, For, createEffect } from "solid-js";
import {
useParams,
A,
@@ -11,10 +11,12 @@ import { createAsync } from "@solidjs/router";
import { getRequestEvent } from "solid-js/web";
import SessionDependantLike from "~/components/blog/SessionDependantLike";
import CommentIcon from "~/components/icons/CommentIcon";
+import { Fire } from "~/components/icons/Fire";
import CommentSectionWrapper from "~/components/blog/CommentSectionWrapper";
import PostBodyClient from "~/components/blog/PostBodyClient";
import type { Comment, CommentReaction, UserPublicData } from "~/types/comment";
import { TerminalSplash } from "~/components/TerminalSplash";
+import { api } from "~/lib/api";
// Server function to fetch post by title
const getPostByTitle = query(
@@ -90,7 +92,6 @@ const getPostByTitle = query(
env: getSafeEnvVariables()
};
- // Parse conditionals in post body
if (post.body) {
try {
post.body = parseConditionals(post.body, conditionalContext);
@@ -212,7 +213,8 @@ const getPostByTitle = query(
reactionArray,
privilegeLevel,
userID,
- sortBy
+ sortBy,
+ reads: post.reads || 0
};
},
"post-by-title"
@@ -235,6 +237,18 @@ export default function PostPage() {
{ deferStream: true }
);
+ // Increment read count when post loads
+ createEffect(() => {
+ const postData = data();
+ if (postData?.post?.id) {
+ api.blog.incrementPostRead
+ .mutate({ postId: postData.post.id })
+ .catch((err) => {
+ console.error("Failed to increment read count:", err);
+ });
+ }
+ });
+
const hasCodeBlock = (str: string): boolean => {
return str.includes("");
};
@@ -313,6 +327,22 @@ export default function PostPage() {