From fb2d2a3b84ddf848106944fb17744a9cd64f606d Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 7 Feb 2024 16:41:35 +0800 Subject: [PATCH] :sparkles: Searchable tags and categories --- pkg/server/posts_api.go | 13 ++- pkg/services/posts.go | 14 +++ pkg/view/src/components/PostItem.tsx | 21 +++-- pkg/view/src/components/PostList.tsx | 6 +- pkg/view/src/index.tsx | 1 + pkg/view/src/pages/search.tsx | 123 +++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 pkg/view/src/pages/search.tsx diff --git a/pkg/server/posts_api.go b/pkg/server/posts_api.go index 2bb1157..1dee6d9 100644 --- a/pkg/server/posts_api.go +++ b/pkg/server/posts_api.go @@ -53,7 +53,6 @@ func getPost(c *fiber.Ctx) error { func listPost(c *fiber.Ctx) error { take := c.QueryInt("take", 0) offset := c.QueryInt("offset", 0) - authorId := c.Query("authorId") tx := database.C. Where("realm_id IS NULL"). @@ -61,13 +60,21 @@ func listPost(c *fiber.Ctx) error { Order("created_at desc") var author models.Account - if len(authorId) > 0 { - if err := database.C.Where(&models.Account{Name: authorId}).First(&author).Error; err != nil { + if len(c.Query("authorId")) > 0 { + if err := database.C.Where(&models.Account{Name: c.Query("authorId")}).First(&author).Error; err != nil { return fiber.NewError(fiber.StatusNotFound, err.Error()) } tx = tx.Where(&models.Post{AuthorID: author.ID}) } + if len(c.Query("category")) > 0 { + tx = services.FilterPostWithCategory(tx, c.Query("category")) + } + + if len(c.Query("tag")) > 0 { + tx = services.FilterPostWithTag(tx, c.Query("tag")) + } + var count int64 if err := tx. Model(&models.Post{}). diff --git a/pkg/services/posts.go b/pkg/services/posts.go index dd40cf1..a7ee191 100644 --- a/pkg/services/posts.go +++ b/pkg/services/posts.go @@ -30,6 +30,20 @@ func PreloadRelatedPost(tx *gorm.DB) *gorm.DB { Preload("ReplyTo.Tags") } +func FilterPostWithCategory(tx *gorm.DB, alias string) *gorm.DB { + prefix := viper.GetString("database.prefix") + return tx.Joins(fmt.Sprintf("JOIN %spost_categories ON %sposts.id = %spost_categories.post_id", prefix, prefix, prefix)). + Joins(fmt.Sprintf("JOIN %scategories ON %scategories.id = %spost_categories.category_id", prefix, prefix, prefix)). + Where(fmt.Sprintf("%scategories.alias = ?", prefix), alias) +} + +func FilterPostWithTag(tx *gorm.DB, alias string) *gorm.DB { + prefix := viper.GetString("database.prefix") + return tx.Joins(fmt.Sprintf("JOIN %spost_tags ON %sposts.id = %spost_tags.post_id", prefix, prefix, prefix)). + Joins(fmt.Sprintf("JOIN %stags ON %stags.id = %spost_tags.tag_id", prefix, prefix, prefix)). + Where(fmt.Sprintf("%stags.alias = ?", prefix), alias) +} + func GetPost(tx *gorm.DB) (*models.Post, error) { var post *models.Post if err := PreloadRelatedPost(tx). diff --git a/pkg/view/src/components/PostItem.tsx b/pkg/view/src/components/PostItem.tsx index 2fdd92c..b59e351 100644 --- a/pkg/view/src/components/PostItem.tsx +++ b/pkg/view/src/components/PostItem.tsx @@ -13,6 +13,7 @@ export default function PostItem(props: { onReply?: (post: any) => void, onEdit?: (post: any) => void, onDelete?: (post: any) => void, + onSearch?: (filter: any) => void, onError: (message: string | null) => void, onReact: () => void }) { @@ -72,16 +73,22 @@ export default function PostItem(props: { -
+
- {item => - #{item.name} - } + {item => + + + {item.name} + + } - {item => - #{item.name} - } + {item => + + + {item.name} + + }
diff --git a/pkg/view/src/components/PostList.tsx b/pkg/view/src/components/PostList.tsx index ca7b91a..c30a841 100644 --- a/pkg/view/src/components/PostList.tsx +++ b/pkg/view/src/components/PostList.tsx @@ -10,7 +10,7 @@ export default function PostList(props: { onRepost?: (post: any) => void, onReply?: (post: any) => void, onEdit?: (post: any) => void, - onUpdate: (pn: number) => Promise, + onUpdate: (pn: number, filter?: any) => Promise, onError: (message: string | null) => void }) { const [loading, setLoading] = createSignal(true); @@ -21,9 +21,9 @@ export default function PostList(props: { const [page, setPage] = createSignal(1); const pageCount = createMemo(() => Math.ceil(postCount() / 10)); - async function readPosts() { + async function readPosts(filter?: any) { setLoading(true); - await props.onUpdate(page()); + await props.onUpdate(page(), filter); setLoading(false); } diff --git a/pkg/view/src/index.tsx b/pkg/view/src/index.tsx index a40d463..e0e9a04 100644 --- a/pkg/view/src/index.tsx +++ b/pkg/view/src/index.tsx @@ -26,6 +26,7 @@ render(() => ( + import("./pages/search.tsx"))} /> import("./pages/realms"))} /> import("./pages/realms/realm.tsx"))} /> import("./pages/account.tsx"))} /> diff --git a/pkg/view/src/pages/search.tsx b/pkg/view/src/pages/search.tsx new file mode 100644 index 0000000..3407191 --- /dev/null +++ b/pkg/view/src/pages/search.tsx @@ -0,0 +1,123 @@ +import { useNavigate, useSearchParams } from "@solidjs/router"; +import { createSignal, Show } from "solid-js"; +import { createStore } from "solid-js/store"; +import PostPublish from "../components/PostPublish.tsx"; +import PostList from "../components/PostList.tsx"; +import { closeModel, openModel } from "../scripts/modals.ts"; + +export default function SearchPage() { + const [searchParams] = useSearchParams(); + + const [error, setError] = createSignal(null); + + const [page, setPage] = createSignal(0); + const [info, setInfo] = createSignal(null); + + const navigate = useNavigate(); + + async function readPosts(pn?: number) { + if (pn) setPage(pn); + const res = await fetch("/api/posts?" + new URLSearchParams({ + take: (10).toString(), + offset: ((page() - 1) * 10).toString(), + ...searchParams + })); + if (res.status !== 200) { + setError(await res.text()); + } else { + setError(null); + setInfo(await res.json()); + } + } + + function setMeta(data: any, field: string, open = true) { + const meta: { [id: string]: any } = { + reposting: null, + replying: null, + editing: null + }; + meta[field] = data; + setPublishMeta(meta); + + if (open) openModel("#post-publish"); + else closeModel("#post-publish"); + } + + const [publishMeta, setPublishMeta] = createStore({ + replying: null, + reposting: null, + editing: null + }); + + function getDescribe() { + let builder = []; + if (searchParams["category"]) { + builder.push("category is #" + searchParams["category"]); + } else if (searchParams["tag"]) { + builder.push("tag is #" + searchParams["tag"]); + } + + return builder.join(" and "); + } + + function back() { + if (window.history.length > 0) { + window.history.back(); + } else { + navigate("/"); + } + } + + return ( + <> +
+ +
+

Search

+
+
+ +
+ + + +
+ + + + + + + + setMeta(item, "reposting")} + onReply={(item) => setMeta(item, "replying")} + onEdit={(item) => setMeta(item, "editing")} + /> + + ); +} \ No newline at end of file