mirror of
https://codeberg.org/VnPower/PixivFE
synced 2024-12-06 19:16:23 +01:00
user page: frequent tags per category
This commit is contained in:
@@ -92,10 +92,10 @@
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="border border-2 border-neutral-700 rounded-5 p-4">
|
||||
<!-- <div class="border border-2 border-neutral-700 rounded-5 p-4">
|
||||
<h3><i class="bi bi-tags me-2"></i>Frequently used tags</h3>
|
||||
<div class="d-flex flex-wrap">
|
||||
{{- range .User.FrequentTags }}
|
||||
{{- range .User.Categories.illustrations.FrequentTags }}
|
||||
<div class="me-2 mb-2">
|
||||
<small>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-wrap">#{{ .Name }}</a>
|
||||
@@ -106,7 +106,8 @@
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,6 +139,22 @@
|
||||
<!-- Illustrations -->
|
||||
{{ if len(.User.Illustrations) != 0 }}
|
||||
<h2 class="d-flex align-items-end">Illustrations<span class="fs-6 bg-charcoal-surface1 rounded px-2 py-1 ms-2">{{ .User.WorkCounts.Illustrations }}</span></h2>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.illustrations.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-2 row-cols-md-4 row-cols-lg-6 g-4">
|
||||
{{- include "fragments/small-tn" .User.Illustrations }}
|
||||
</div>
|
||||
@@ -155,8 +172,23 @@
|
||||
<hr class="my-4">
|
||||
<h2 class="d-flex align-items-end">Manga<span class="fs-6 bg-charcoal-surface1 rounded px-2 py-1 ms-2">{{ .User.WorkCounts.Manga }}</span></h2>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.manga.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{- if len(.User.MangaSeries) != 0 }}
|
||||
<div class="overflow-scroll mb-5" id="horizontal-scroll">
|
||||
<div class="overflow-scroll mb-4" id="horizontal-scroll">
|
||||
<div class="row flex-nowrap">
|
||||
{{- range .User.MangaSeries }}
|
||||
{{- include "fragments/mangaseries-tn" . }}
|
||||
@@ -181,8 +213,23 @@
|
||||
<hr class="my-4">
|
||||
<h2 class="d-flex align-items-end">Novels<span class="fs-6 bg-charcoal-surface1 rounded px-2 py-1 ms-2">{{ .User.WorkCounts.Novels }}</span></h2>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.novels.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{- if len(.User.NovelSeries) != 0 }}
|
||||
<div class="overflow-scroll mb-5" id="horizontal-scroll">
|
||||
<div class="overflow-scroll mb-4" id="horizontal-scroll">
|
||||
<div class="row flex-nowrap">
|
||||
{{- range .User.NovelSeries }}
|
||||
{{- include "fragments/novelseries-tn" . }}
|
||||
@@ -218,6 +265,22 @@
|
||||
{{- combinedUrl := "/artworks-multi/" + joinArtworkIds(.User.Illustrations) }}
|
||||
<!-- <a href="{{combinedUrl}}" class="custom-btn-secondary">View all</a> -->
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.illustrations.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-2 row-cols-md-4 row-cols-lg-6 g-4">
|
||||
{{- include "fragments/small-tn" .User.Illustrations }}
|
||||
</div>
|
||||
@@ -231,6 +294,21 @@
|
||||
<!-- <a href="{{combinedUrl}}" class="custom-btn-secondary">View all</a> -->
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.manga.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{- if len(.User.MangaSeries) != 0 }}
|
||||
<div class="overflow-scroll mb-4" id="horizontal-scroll">
|
||||
<div class="row flex-nowrap">
|
||||
@@ -251,6 +329,22 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="d-flex align-items-end mb-0">Novels<span class="fs-6 bg-charcoal-surface1 rounded px-2 py-1 ms-2">{{ .User.WorkCounts.Novels }}</span></h2>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-nowrap overflow-scroll mb-3">
|
||||
{{- range .User.Categories.novels.FrequentTags }}
|
||||
<div class="bg-charcoal-surface1 rounded px-3 py-2 me-2 mb-2 w-auto">
|
||||
<small class="d-flex flex-column align-items-center">
|
||||
<span>
|
||||
<a href="/tags/{{ escapeString(.Name) }}" class="text-primary-emphasis fw-semibold text-decoration-none text-nowrap">#{{ .Name }}</a>
|
||||
</span>
|
||||
{{- if .TranslatedName }}
|
||||
<small class="text-body-secondary fw-medium text-nowrap">({{ .TranslatedName }})</small>
|
||||
{{- end }}
|
||||
</small>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-lg-2 g-4">
|
||||
{{- range .User.Novels }}
|
||||
<div class="col mt-1">
|
||||
|
||||
+60
-110
@@ -15,17 +15,18 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// UserWorkCategory is a struct that represents a category of user work with a page limit.
|
||||
// UserWorkCategory is a struct that represents a category of user work with a page limit and frequent tags.
|
||||
//
|
||||
// pixivfe internal data type. not used by pixiv.
|
||||
type UserWorkCategory struct {
|
||||
Value string
|
||||
PageLimit int
|
||||
Value string
|
||||
PageLimit int
|
||||
FrequentTags []FrequentTag
|
||||
}
|
||||
|
||||
// NewUserWorkCategory creates UserWorkCategory instances
|
||||
func NewUserWorkCategory(value string) UserWorkCategory {
|
||||
return UserWorkCategory{Value: value, PageLimit: 0}
|
||||
return UserWorkCategory{Value: value, PageLimit: 0, FrequentTags: []FrequentTag{}}
|
||||
}
|
||||
|
||||
func (c *UserWorkCategory) SetPageLimit(limit int) {
|
||||
@@ -103,6 +104,8 @@ type User struct {
|
||||
|
||||
// Used to display the number of works for a given category
|
||||
WorkCounts WorkCounts
|
||||
|
||||
Categories map[string]*UserWorkCategory
|
||||
}
|
||||
|
||||
// GetUserProfile retrieves the user profile, including counts, artworks/bookmarks, and social data.
|
||||
@@ -296,15 +299,45 @@ func getPopulatedWorks(auditor *audit.Auditor, r *http.Request, user *User, id s
|
||||
auditor.Logger.Debug("NovelBriefs populated", zap.Int("novelsCount", len(novels)))
|
||||
}
|
||||
|
||||
// Fetch a user's frequently used tags if requested
|
||||
// Initialize the user categories
|
||||
user.Categories = map[string]*UserWorkCategory{
|
||||
"illustrations": {Value: "illustrations"},
|
||||
"manga": {Value: "manga"},
|
||||
"novels": {Value: "novels"},
|
||||
}
|
||||
|
||||
// Define the categories with their IDs
|
||||
categories := []struct {
|
||||
name string
|
||||
ids string
|
||||
workCategory *UserWorkCategory
|
||||
}{
|
||||
{
|
||||
name: "illustrations",
|
||||
ids: illustrationIDs,
|
||||
workCategory: user.Categories["illustrations"],
|
||||
},
|
||||
{
|
||||
name: "manga",
|
||||
ids: mangaIDs,
|
||||
workCategory: user.Categories["manga"],
|
||||
},
|
||||
{
|
||||
name: "novels",
|
||||
ids: novelIDs,
|
||||
workCategory: user.Categories["novels"],
|
||||
},
|
||||
}
|
||||
|
||||
// Fetch frequent tags
|
||||
if getTags {
|
||||
auditor.Logger.Debug("Fetching frequent tags")
|
||||
user.FrequentTags, err = populateFrequentTags(auditor, r, id, currentCategory, illustrationIDs, mangaIDs, novelIDs)
|
||||
err := populateFrequentTags(auditor, r, id, categories)
|
||||
if err != nil {
|
||||
auditor.SugaredLogger.Error("Failed to populate frequent tags", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
auditor.Logger.Debug("Frequent tags populated", zap.Int("tagsCount", len(user.FrequentTags)))
|
||||
auditor.Logger.Debug("Frequent tags populated")
|
||||
}
|
||||
|
||||
// Handle series data
|
||||
@@ -330,109 +363,24 @@ func populateFrequentTags(
|
||||
auditor *audit.Auditor,
|
||||
r *http.Request,
|
||||
id string,
|
||||
currentCategory *UserWorkCategory,
|
||||
illustrationIDs string,
|
||||
mangaIDs string,
|
||||
novelIDs string,
|
||||
) ([]FrequentTag, error) {
|
||||
var (
|
||||
primaryTagsIDs []string // For illustrationIDs and mangaIDs
|
||||
secondaryTagsIDs []string // For novelIDs
|
||||
)
|
||||
|
||||
// Append illustrationIDs to primaryTagsIDs if applicable
|
||||
if illustrationIDs != "" {
|
||||
primaryTagsIDs = append(primaryTagsIDs, illustrationIDs)
|
||||
auditor.Logger.Debug(
|
||||
"illustrationIDs found, appended to primaryTagsIDs",
|
||||
// zap.Strings("primaryTagsIDs", primaryTagsIDs),
|
||||
// zap.String("illustrationIDs", illustrationIDs),
|
||||
)
|
||||
}
|
||||
|
||||
// Append mangaIDs to primaryTagsIDs if applicable
|
||||
if mangaIDs != "" {
|
||||
primaryTagsIDs = append(primaryTagsIDs, mangaIDs)
|
||||
auditor.Logger.Debug(
|
||||
"mangaIDs found, appended to primaryTagsIDs",
|
||||
// zap.Strings("primaryTagsIDs", primaryTagsIDs),
|
||||
// zap.String("mangaIDs", mangaIDs),
|
||||
)
|
||||
}
|
||||
|
||||
// Append novelIDs to secondaryTagsIDs if applicable
|
||||
if novelIDs != "" {
|
||||
secondaryTagsIDs = append(secondaryTagsIDs, novelIDs)
|
||||
auditor.Logger.Debug(
|
||||
"novelIDs found, appended to secondaryTagsIDs",
|
||||
// zap.Strings("secondaryTagsIDs", secondaryTagsIDs),
|
||||
// zap.String("novelIDs", novelIDs),
|
||||
)
|
||||
}
|
||||
|
||||
// Function to count the number of items based on delimiter
|
||||
countIDs := func(ids []string) int {
|
||||
count := 0
|
||||
for _, idStr := range ids {
|
||||
// Each idStr contains multiple "&ids[]=" entries, so we split by "&ids[]=" and count non-empty parts
|
||||
parts := strings.Split(idStr, "&ids[]=")
|
||||
for _, part := range parts {
|
||||
if part != "" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
categories []struct {
|
||||
name string
|
||||
ids string
|
||||
workCategory *UserWorkCategory
|
||||
},
|
||||
) error {
|
||||
for _, cat := range categories {
|
||||
if cat.ids == "" {
|
||||
continue
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Count the number of items in each group
|
||||
primaryCount := countIDs(primaryTagsIDs)
|
||||
secondaryCount := countIDs(secondaryTagsIDs)
|
||||
|
||||
auditor.Logger.Debug(
|
||||
"Counts of primary and secondary tags",
|
||||
zap.Int("primaryCount", primaryCount),
|
||||
zap.Int("secondaryCount", secondaryCount),
|
||||
)
|
||||
|
||||
var finalTagsIDs []string
|
||||
|
||||
// Decide which tagsIDs to use based on the count
|
||||
if primaryCount > secondaryCount {
|
||||
finalTagsIDs = primaryTagsIDs
|
||||
auditor.Logger.Debug(
|
||||
"Selected primaryTagsIDs as finalTagsIDs",
|
||||
zap.Strings("finalTagsIDs", finalTagsIDs),
|
||||
)
|
||||
} else {
|
||||
finalTagsIDs = secondaryTagsIDs
|
||||
auditor.Logger.Debug(
|
||||
"Selected secondaryTagsIDs as finalTagsIDs",
|
||||
zap.Strings("finalTagsIDs", finalTagsIDs),
|
||||
)
|
||||
}
|
||||
|
||||
// Check if any tags were found
|
||||
if len(finalTagsIDs) != 0 {
|
||||
// Concatenate IDs to form a single string
|
||||
ids := strings.Join(finalTagsIDs, "")
|
||||
frequentTags, err := fetchFrequentTags(auditor, r, ids, currentCategory)
|
||||
tags, err := fetchFrequentTags(auditor, r, cat.ids, cat.workCategory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user frequent tags: %w", err)
|
||||
auditor.SugaredLogger.Error("Failed to fetch frequent tags", zap.String("category", cat.name), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return frequentTags, nil
|
||||
cat.workCategory.FrequentTags = tags
|
||||
}
|
||||
|
||||
auditor.Logger.Warn(
|
||||
"No tags found to fetch, setting FrequentTags to empty slice",
|
||||
zap.String("currentCategory", currentCategory.Value),
|
||||
zap.Strings("primaryTagsIDs", primaryTagsIDs),
|
||||
zap.Strings("secondaryTagsIDs", secondaryTagsIDs),
|
||||
zap.String("illustrationIDs", illustrationIDs),
|
||||
zap.String("mangaIDs", mangaIDs),
|
||||
zap.String("novelIDs", novelIDs),
|
||||
)
|
||||
return []FrequentTag{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// The HasID interface allows for generic handling of the ArtworkBrief
|
||||
@@ -783,15 +731,17 @@ func handleSeriesData(auditor *audit.Auditor, mangaSeriesData, novelSeriesData j
|
||||
}
|
||||
|
||||
// fetchFrequentTags fetches a user's frequently used tags, based on category.
|
||||
func fetchFrequentTags(auditor *audit.Auditor, r *http.Request, ids string, currentCategory *UserWorkCategory) ([]FrequentTag, error) {
|
||||
func fetchFrequentTags(auditor *audit.Auditor, r *http.Request, ids string, workCategory *UserWorkCategory) ([]FrequentTag, error) {
|
||||
var tags []FrequentTag
|
||||
var URL string
|
||||
|
||||
// TODO: If overall, request both?
|
||||
if currentCategory != &CategoryNovels {
|
||||
switch workCategory.Value {
|
||||
case "illustrations", "manga":
|
||||
URL = GetUserFrequentArtworkTagsURL(ids)
|
||||
} else {
|
||||
case "novels":
|
||||
URL = GetUserFrequentNovelTagsURL(ids)
|
||||
default:
|
||||
return tags, fmt.Errorf("unsupported category: %s", workCategory.Value)
|
||||
}
|
||||
|
||||
// Return early if there are no IDs
|
||||
|
||||
Reference in New Issue
Block a user