feat: add related tags

This commit is contained in:
Damillora 2022-04-16 16:32:04 +07:00
parent e175d12ca7
commit ea5ba769df
8 changed files with 179 additions and 35 deletions

View File

@ -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)

View File

@ -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
}

View File

@ -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);

View 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>

View File

@ -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

View File

@ -1,17 +1,21 @@
@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;
}
.pre-line {
white-space: pre-line;
}
.svelte-tags-input-matchs-parent {
z-index: 2000;
}

View File

@ -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">

View File

@ -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}