fix sort and filter
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createSignal, Show } from "solid-js";
|
||||
import { useSearchParams } from "@solidjs/router";
|
||||
import type {
|
||||
Comment,
|
||||
CommentReaction,
|
||||
@@ -42,8 +43,10 @@ interface CommentSectionProps {
|
||||
}
|
||||
|
||||
export default function CommentSection(props: CommentSectionProps) {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [selectedSorting, setSelectedSorting] = createSignal<SortingMode>(
|
||||
COMMENT_SORTING_OPTIONS[0].val
|
||||
(searchParams.sortBy as SortingMode) || COMMENT_SORTING_OPTIONS[0].val
|
||||
);
|
||||
|
||||
const hasComments = () =>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createSignal, createEffect, For, Show, createMemo } from "solid-js";
|
||||
import { createSignal, createEffect, For, Show } from "solid-js";
|
||||
import type { CommentSortingProps } from "~/types/comment";
|
||||
import { sortComments } from "~/lib/comment-utils";
|
||||
import CommentBlock from "./CommentBlock";
|
||||
|
||||
export default function CommentSorting(props: CommentSortingProps) {
|
||||
@@ -35,17 +34,9 @@ export default function CommentSorting(props: CommentSortingProps) {
|
||||
}
|
||||
};
|
||||
|
||||
// Memoized sorted comments
|
||||
const sortedComments = createMemo(() => {
|
||||
return sortComments(
|
||||
props.topLevelComments,
|
||||
props.selectedSorting.val,
|
||||
props.reactionMap
|
||||
);
|
||||
});
|
||||
|
||||
// Comments are already sorted from server, no need for client-side sorting
|
||||
return (
|
||||
<For each={sortedComments()}>
|
||||
<For each={props.topLevelComments}>
|
||||
{(topLevelComment) => (
|
||||
<div
|
||||
onClick={() => checkForDoubleClick(topLevelComment.id)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { For, Show, createSignal } from "solid-js";
|
||||
import { useNavigate, useLocation } from "@solidjs/router";
|
||||
import type { CommentSortingSelectProps, SortingMode } from "~/types/comment";
|
||||
import Check from "~/components/icons/Check";
|
||||
import UpDownArrows from "~/components/icons/UpDownArrows";
|
||||
@@ -12,6 +13,8 @@ const SORTING_OPTIONS: { val: SortingMode; label: string }[] = [
|
||||
|
||||
export default function CommentSortingSelect(props: CommentSortingSelectProps) {
|
||||
const [isOpen, setIsOpen] = createSignal(false);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const selectedLabel = () => {
|
||||
const option = SORTING_OPTIONS.find(
|
||||
@@ -23,6 +26,14 @@ export default function CommentSortingSelect(props: CommentSortingSelectProps) {
|
||||
const handleSelect = (mode: SortingMode) => {
|
||||
props.setSorting(mode);
|
||||
setIsOpen(false);
|
||||
|
||||
// Update URL with sortBy parameter
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("sortBy", mode);
|
||||
navigate(`${location.pathname}?${url.searchParams.toString()}#comments`, {
|
||||
scroll: false,
|
||||
replace: true
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,31 +1,128 @@
|
||||
import { For, Show } from "solid-js";
|
||||
import { For, Show, createMemo } from "solid-js";
|
||||
import Card, { Post } from "./Card";
|
||||
|
||||
export interface Tag {
|
||||
value: string;
|
||||
post_id: number;
|
||||
}
|
||||
|
||||
export interface PostSortingProps {
|
||||
posts: Post[];
|
||||
tags: Tag[];
|
||||
privilegeLevel: "anonymous" | "admin" | "user";
|
||||
filters?: string;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostSorting Component
|
||||
*
|
||||
* Note: This component has been simplified - filtering and sorting
|
||||
* are now handled server-side via the blog.getPosts tRPC query.
|
||||
*
|
||||
* This component now only renders the posts that have already been
|
||||
* filtered and sorted by the server.
|
||||
*/
|
||||
export default function PostSorting(props: PostSortingProps) {
|
||||
// Build set of tags that are ALLOWED (not filtered out)
|
||||
const allowedTags = createMemo(() => {
|
||||
const filterList = props.filters?.split("|").filter(Boolean) || [];
|
||||
|
||||
// If no filters set, all tags are allowed
|
||||
if (filterList.length === 0) {
|
||||
return new Set(props.tags.map((t) => t.value.slice(1)));
|
||||
}
|
||||
|
||||
// Build set of tags that are checked (allowed to show)
|
||||
const allTags = new Set(props.tags.map((t) => t.value.slice(1)));
|
||||
const filteredOutTags = new Set(filterList);
|
||||
|
||||
const allowed = new Set<string>();
|
||||
allTags.forEach((tag) => {
|
||||
if (!filteredOutTags.has(tag)) {
|
||||
allowed.add(tag);
|
||||
}
|
||||
});
|
||||
|
||||
return allowed;
|
||||
});
|
||||
|
||||
// Get posts that have at least one allowed tag
|
||||
const filteredPosts = createMemo(() => {
|
||||
const allowed = allowedTags();
|
||||
|
||||
// If all tags are allowed, show all posts
|
||||
if (
|
||||
allowed.size ===
|
||||
props.tags
|
||||
.map((t) => t.value.slice(1))
|
||||
.filter((v, i, a) => a.indexOf(v) === i).length
|
||||
) {
|
||||
return props.posts;
|
||||
}
|
||||
|
||||
// Build map of post_id -> tags for that post
|
||||
const postTags = new Map<number, Set<string>>();
|
||||
props.tags.forEach((tag) => {
|
||||
if (!postTags.has(tag.post_id)) {
|
||||
postTags.set(tag.post_id, new Set());
|
||||
}
|
||||
postTags.get(tag.post_id)!.add(tag.value.slice(1));
|
||||
});
|
||||
|
||||
// Keep posts that have at least one allowed tag
|
||||
return props.posts.filter((post) => {
|
||||
const tags = postTags.get(post.id);
|
||||
if (!tags) return false; // Post has no tags
|
||||
|
||||
// Check if post has at least one allowed tag
|
||||
for (const tag of tags) {
|
||||
if (allowed.has(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
const sortedPosts = createMemo(() => {
|
||||
let sorted = [...filteredPosts()];
|
||||
|
||||
switch (props.sort) {
|
||||
case "newest":
|
||||
sorted.reverse(); // Posts come oldest first from DB
|
||||
break;
|
||||
case "oldest":
|
||||
// Already in oldest order from DB
|
||||
break;
|
||||
case "most_liked":
|
||||
sorted.sort((a, b) => (b.total_likes || 0) - (a.total_likes || 0));
|
||||
break;
|
||||
case "most_read":
|
||||
sorted.sort((a, b) => (b.reads || 0) - (a.reads || 0));
|
||||
break;
|
||||
case "most_comments":
|
||||
sorted.sort(
|
||||
(a, b) => (b.total_comments || 0) - (a.total_comments || 0)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
sorted.reverse(); // Default to newest
|
||||
}
|
||||
|
||||
return sorted;
|
||||
});
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={props.posts.length > 0}
|
||||
when={sortedPosts().length > 0}
|
||||
fallback={
|
||||
<div class="pt-12 text-center text-2xl tracking-wide italic">
|
||||
No posts found!
|
||||
</div>
|
||||
<Show
|
||||
when={props.posts.length > 0}
|
||||
fallback={
|
||||
<div class="pt-12 text-center text-2xl tracking-wide italic">
|
||||
No posts found!
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class="pt-12 text-center text-2xl tracking-wide italic">
|
||||
All posts filtered out!
|
||||
</div>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<For each={props.posts}>
|
||||
<For each={sortedPosts()}>
|
||||
{(post) => (
|
||||
<div class="my-4">
|
||||
<Card post={post} privilegeLevel={props.privilegeLevel} />
|
||||
|
||||
@@ -65,8 +65,17 @@ 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}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="relative">
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
@@ -78,8 +87,17 @@ export default function TagSelector(props: TagSelectorProps) {
|
||||
<Show when={showingMenu()}>
|
||||
<div
|
||||
ref={menuRef}
|
||||
class="bg-surface0 absolute z-50 mt-12 rounded-lg py-2 pr-4 pl-2 shadow-lg"
|
||||
class="bg-surface0 absolute top-full left-0 z-50 mt-2 rounded-lg py-2 pr-4 pl-2 shadow-lg"
|
||||
>
|
||||
<div class="border-overlay0 mb-2 flex justify-center border-b pb-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleUncheckAll}
|
||||
class="text-text hover:text-red text-xs font-medium underline"
|
||||
>
|
||||
Uncheck All
|
||||
</button>
|
||||
</div>
|
||||
<For each={Object.entries(props.tagMap)}>
|
||||
{([key, value]) => (
|
||||
<div class="mx-auto my-2 flex">
|
||||
@@ -98,6 +116,6 @@ export default function TagSelector(props: TagSelectorProps) {
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user