From 9cc682bda40e21d5827d631a5b38af57f5981b46 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Tue, 23 Dec 2025 01:02:55 -0500 Subject: [PATCH] filtering cleanup, hits rendered --- src/components/blog/TagSelector.tsx | 77 ++++++++++++++++++++++++----- src/components/icons/Fire.tsx | 19 +++++++ src/routes/blog/[title]/index.tsx | 36 ++++++++++++-- src/server/api/routers/blog.ts | 16 +++++- 4 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 src/components/icons/Fire.tsx 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() {