mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-12-26 09:23:45 +00:00
feat: add related tags
This commit is contained in:
parent
e175d12ca7
commit
ea5ba769df
@ -19,6 +19,10 @@ func InitializeTagRoutes(g *gin.Engine) {
|
||||
unprotected.GET("/", tagGet)
|
||||
unprotected.GET("/:tag", tagGetOne)
|
||||
}
|
||||
related := g.Group("/api/tag-related")
|
||||
{
|
||||
related.GET("/:tag", tagGetRelated)
|
||||
}
|
||||
protected := g.Group("/api/tag").Use(middleware.AuthMiddleware())
|
||||
{
|
||||
protected.PUT("/:tag/note", tagUpdateNote)
|
||||
@ -51,6 +55,20 @@ func tagGetOne(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func tagGetRelated(c *gin.Context) {
|
||||
tag := c.Param("tag")
|
||||
tagObj, err := services.GetRelatedTags(tag)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: err.Error(),
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tagObj)
|
||||
}
|
||||
|
||||
func tagAutocomplete(c *gin.Context) {
|
||||
tags := services.GetTagAutocomplete()
|
||||
c.JSON(http.StatusOK, tags)
|
||||
|
@ -2,6 +2,7 @@ package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Damillora/Shioriko/pkg/database"
|
||||
@ -225,3 +226,41 @@ func UpdateTag(tagString string, model models.TagUpdateModel) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetRelatedTags(tagSyntax string) ([]models.TagListItem, error) {
|
||||
tags, err := FindTag(tagSyntax)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var postIds []string
|
||||
database.DB.
|
||||
Model(&tags).
|
||||
Joins("join post_tags on post_tags.tag_id = tags.id").
|
||||
Select("post_tags.post_id").
|
||||
Where("post_tags.tag_id = ?", tags.ID).
|
||||
Find(&postIds)
|
||||
|
||||
var tagIds []string
|
||||
database.DB.
|
||||
Model(&tags).
|
||||
Joins("join post_tags on post_tags.tag_id = tags.id").
|
||||
Select("post_tags.tag_id").
|
||||
Where("post_tags.post_id IN ?", postIds).
|
||||
Find(&tagIds)
|
||||
fmt.Printf("%+v", tags)
|
||||
fmt.Println()
|
||||
fmt.Printf("%v", tagIds)
|
||||
fmt.Println()
|
||||
|
||||
var tagInfo []models.TagListItem
|
||||
database.DB.Model(&tags).
|
||||
Joins("join tag_types on tag_types.id = tags.tag_type_id").
|
||||
Joins("left join post_tags on post_tags.tag_id = tags.id").
|
||||
Select("tags.id as tag_id, tags.name as tag_name, tag_types.name as tag_type, count(post_tags.post_id) as post_count").
|
||||
Group("tags.id, tags.name, tag_types.name").
|
||||
Order("post_count DESC").
|
||||
Find(&tagInfo, tagIds)
|
||||
|
||||
return tagInfo, nil
|
||||
}
|
||||
|
@ -48,6 +48,11 @@ export async function getTag({ tag }) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getRelatedTags({ tag }) {
|
||||
const endpoint = url + "/api/tag-related/" + tag;
|
||||
const response = await axios.get(endpoint);
|
||||
return response.data;
|
||||
}
|
||||
export async function getTagAutocomplete() {
|
||||
const endpoint = url + "/api/tag-autocomplete";
|
||||
const response = await axios.get(endpoint);
|
||||
|
24
web/app/src/components/Post/PostGallery.svelte
Normal file
24
web/app/src/components/Post/PostGallery.svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { Link } from "svelte-routing";
|
||||
|
||||
export let posts = [];
|
||||
</script>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post, i (post.id)}
|
||||
<div class="column is-one-quarter">
|
||||
<div class="block">
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<Link to="/post/{post.id}">
|
||||
<img alt={post.id} src={post.thumbnail_path} />
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
@ -1,10 +1,24 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { Link } from "svelte-routing";
|
||||
import { getRelatedTags } from "../../api.js";
|
||||
import AuthCheck from "../../AuthCheck.svelte";
|
||||
import TagLinkNumbered from "../../TagLinkNumbered.svelte";
|
||||
|
||||
export let tag;
|
||||
export let data;
|
||||
export let toggleRenameMenu;
|
||||
let related_tags = [];
|
||||
const getData = async () => {
|
||||
related_tags = await getRelatedTags({ tag });
|
||||
related_tags = related_tags
|
||||
.filter((x) => x.tagName != tag)
|
||||
.sort((a, b) => b.postCount - a.postCount);
|
||||
};
|
||||
onMount(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="panel is-primary">
|
||||
@ -29,6 +43,26 @@
|
||||
{data.postCount} (<Link to="/posts?tags={tag}">Browse</Link>)
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-block column">
|
||||
<div class="row">
|
||||
<strong>Related Tags:</strong>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="menu">
|
||||
<ul class="menu-list">
|
||||
{#each related_tags as tag (tag)}
|
||||
<li>
|
||||
<TagLinkNumbered
|
||||
class=""
|
||||
tag={tag.tagType + ":" + tag.tagName}
|
||||
num={tag.postCount}
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AuthCheck>
|
||||
<div class="panel-block column">
|
||||
<button
|
||||
|
@ -1,16 +1,20 @@
|
||||
@import '../node_modules/bulma/sass/utilities/_all';
|
||||
@import '../node_modules/bulma/sass/base/_all';
|
||||
@import '../node_modules/bulma/sass/elements/_all';
|
||||
@import '../node_modules/bulma/sass/form/_all';
|
||||
@import '../node_modules/bulma/sass/components/_all';
|
||||
@import '../node_modules/bulma/sass/grid/_all';
|
||||
@import '../node_modules/bulma/sass/helpers/_all';
|
||||
@import '../node_modules/bulma/sass/layout/_all';
|
||||
@import '../node_modules/bytemd/dist/index.css';
|
||||
@import "../node_modules/bulma/sass/utilities/_all";
|
||||
@import "../node_modules/bulma/sass/base/_all";
|
||||
@import "../node_modules/bulma/sass/elements/_all";
|
||||
@import "../node_modules/bulma/sass/form/_all";
|
||||
@import "../node_modules/bulma/sass/components/_all";
|
||||
@import "../node_modules/bulma/sass/grid/_all";
|
||||
@import "../node_modules/bulma/sass/helpers/_all";
|
||||
@import "../node_modules/bulma/sass/layout/_all";
|
||||
@import "../node_modules/bytemd/dist/index.css";
|
||||
|
||||
.tile.is-multiline {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pre-line {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.svelte-tags-input-matchs-parent {
|
||||
z-index: 2000;
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script>
|
||||
import { getPostSearchTag, getTagAutocomplete } from "../../api.js";
|
||||
import { getPostSearchTag, getTag, getTagAutocomplete } from "../../api.js";
|
||||
import { Link, navigate } from "svelte-routing";
|
||||
import TagLinkNumbered from "../../TagLinkNumbered.svelte";
|
||||
import PostGallery from "../../components/Post/PostGallery.svelte";
|
||||
import queryString from "query-string";
|
||||
import Tags from "svelte-tags-input";
|
||||
import { paginate } from "../../simple-pagination.js";
|
||||
@ -16,6 +17,7 @@
|
||||
let posts = [];
|
||||
let postCount = 0;
|
||||
let tags = [];
|
||||
let tagInfo = null;
|
||||
let categorizedTags = {};
|
||||
|
||||
const getData = async () => {
|
||||
@ -33,6 +35,10 @@
|
||||
postCount = 0;
|
||||
pagination = paginate(page, totalPages);
|
||||
}
|
||||
|
||||
if (searchTerms.length == 1) {
|
||||
tagInfo = await getTag({ tag: searchTerms[0] });
|
||||
}
|
||||
};
|
||||
let queryParams;
|
||||
|
||||
@ -51,6 +57,7 @@
|
||||
searchTerms = queryParams.tags.split(" ");
|
||||
} else {
|
||||
searchTerms = [];
|
||||
tagInfo = null;
|
||||
}
|
||||
posts = [];
|
||||
page = 1;
|
||||
@ -103,6 +110,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
{#if tagInfo}
|
||||
<div class="panel is-info">
|
||||
<p class="panel-heading">
|
||||
Tag:
|
||||
{tagInfo.tagName.split("_").join(" ")}
|
||||
</p>
|
||||
{#if tagInfo.tagNote}
|
||||
<div class="panel-block column ">
|
||||
<div class="content pre-line">
|
||||
{tagInfo.tagNote}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="panel-block column">
|
||||
<Link
|
||||
class="button is-primary"
|
||||
to="/tags/{tagInfo.tagName}">View Tag</Link
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="panel is-primary">
|
||||
<div class="panel-heading">Tags</div>
|
||||
<div class="panel-block column">
|
||||
@ -127,28 +155,7 @@
|
||||
<div class="column is-two-thirds">
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-full">
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post, i (post.id)}
|
||||
<div class="column is-one-quarter">
|
||||
<div class="block">
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<Link
|
||||
to="/post/{post.id}"
|
||||
>
|
||||
<img
|
||||
alt={post.id}
|
||||
src={post.thumbnail_path}
|
||||
/>
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<PostGallery {posts} />
|
||||
</div>
|
||||
|
||||
<div class="column is-full">
|
||||
|
@ -1,18 +1,29 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { getTag } from "../../api";
|
||||
import { getTag, getPostSearchTag } from "../../api";
|
||||
import EditTagNotesPanel from "../../components/TagNotes/EditTagNotesPanel.svelte";
|
||||
import ViewTagNotesPanel from "../../components/TagNotes/ViewTagNotesPanel.svelte";
|
||||
import ViewTagPanel from "../../components/Tag/ViewTagPanel.svelte";
|
||||
import EditTagPanel from "../../components/Tag/EditTagPanel.svelte";
|
||||
import PostGallery from "../../components/Post/PostGallery.svelte";
|
||||
|
||||
import { Link } from "svelte-routing";
|
||||
|
||||
export let tag;
|
||||
let data;
|
||||
let posts = [];
|
||||
|
||||
const getData = async () => {
|
||||
if (tag) {
|
||||
data = await getTag({ tag });
|
||||
const response = await getPostSearchTag({
|
||||
page: 1,
|
||||
q: tag,
|
||||
});
|
||||
if (response.posts) {
|
||||
posts = response.posts.slice(0, 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,6 +74,8 @@
|
||||
{:else}
|
||||
<ViewTagNotesPanel {data} {toggleEditMenu} />
|
||||
{/if}
|
||||
<h1 class="title">Posts</h1>
|
||||
<PostGallery {posts} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user