Skip to content

Commit

Permalink
Merge branch add-categories-page
Browse files Browse the repository at this point in the history
  • Loading branch information
vednoc committed Oct 2, 2023
2 parents 257aa9a + 768a384 commit 7d7f114
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 38 deletions.
15 changes: 13 additions & 2 deletions handlers/core/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,27 @@ import (
"userstyles.world/modules/storage"
)

func wrapQuery(s string) string {
switch {
case strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`):
return s
case strings.Contains(s, "-") || strings.Contains(s, "."):
return `"` + s + `"`
default:
return s
}
}

func Search(c *fiber.Ctx) error {
u, _ := jwt.User(c)
c.Locals("User", u)
c.Locals("Title", "Search userstyles")
c.Locals("Canonical", "search")

keyword := strings.TrimSpace(c.Query("q"))
keyword := wrapQuery(strings.TrimSpace(c.Query("q")))
c.Locals("Keyword", keyword)

category := strings.TrimSpace(c.Query("category"))
category := wrapQuery(strings.TrimSpace(c.Query("category")))
c.Locals("Category", category)

query := keyword
Expand Down
2 changes: 1 addition & 1 deletion handlers/style/ban.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,6 @@ func sendRemovalEmail(user *storage.User, style *models.APIStyle, entry models.L

title := "Your style has been removed"
if err := email.Send("style/ban", user.Email, title, args); err != nil {
log.Warn.Printf("Failed to email author for style %d: %s\n", style.ID, err)
log.Warn.Printf("Failed to email %d: %s\n", user.ID, err)
}
}
44 changes: 44 additions & 0 deletions handlers/style/category.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package style

import (
"github.com/gofiber/fiber/v2"

"userstyles.world/handlers/jwt"
"userstyles.world/models"
"userstyles.world/modules/config"
"userstyles.world/modules/storage"
)

func GetCategory(c *fiber.Ctx) error {
u, _ := jwt.User(c)

c.Locals("Title", "Categories")
c.Locals("User", u)

page, err := models.IsValidPage(c.Query("page"))
if err != nil {
c.Locals("Title", "Invalid page size")
return c.Render("err", fiber.Map{})
}

count, err := storage.CountStyleCategories()
if err != nil {
c.Locals("Title", "Failed to count categories")
return c.Render("err", fiber.Map{})
}

p := models.NewPagination(page, count, "", c.Path())
if p.OutOfBounds() {
return c.Redirect(p.URL(p.Now), 302)
}
c.Locals("Pagination", p)

cat, err := storage.GetStyleCategories(page, config.AppPageMaxItems)
if err != nil {
c.Locals("Title", "Failed to find categories")
return c.Render("err", fiber.Map{})
}
c.Locals("Categories", cat)

return c.Render("style/category", fiber.Map{})
}
11 changes: 2 additions & 9 deletions handlers/style/promote.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ import (
)

func sendPromotionEmail(style *models.APIStyle, user *models.User, mod string) {
user, err := models.FindUserByID(strconv.Itoa(int(style.UserID)))
if err != nil {
log.Warn.Printf("Couldn't find user %d: %s\n", style.UserID, err)
return
}

args := fiber.Map{
"User": user,
"Style": style,
Expand All @@ -29,9 +23,8 @@ func sendPromotionEmail(style *models.APIStyle, user *models.User, mod string) {
}

title := "Your style has been featured"
err = email.Send("style/promote", user.Email, title, args)
if err != nil {
log.Warn.Printf("Failed to send an email: %s\n", err)
if err := email.Send("style/promote", user.Email, title, args); err != nil {
log.Warn.Printf("Failed to email %d: %s\n", user.ID, err)
}
}

Expand Down
1 change: 1 addition & 0 deletions handlers/style/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
func Routes(app *fiber.App) {
r := app.Group("/")
r.Get("/explore", GetExplore)
r.Get("/category/:category?", GetCategory)
r.Get("/style/:id/:name?", middleware.Alert, GetStylePage)
r.Get("/add", jwtware.Protected, CreateGet)
r.Post("/add", jwtware.Protected, CreatePost)
Expand Down
54 changes: 54 additions & 0 deletions modules/storage/style_category.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package storage

import (
"userstyles.world/modules/database"
)

// Category represents a single category.
type Category struct {
Category string
Count int
}

// URL returns a link to a category page.
func (c *Category) URL() string {
return "/search?category=" + c.Category
}

// Categories is an alias for a slice of Category structs.
type Categories []*Category

// GetStyleCategories returns a slice of categories for category page.
func GetStyleCategories(page, size int) (c Categories, err error) {
offset := (page - 1) * size

err = database.Conn.
Select("LOWER(category) AS category, COUNT(LOWER(category)) AS count").
Table("styles").
Group("LOWER(category)").
Order("count DESC").
Offset(offset).
Limit(size).
Find(&c, notDeleted).
Error
if err != nil {
return nil, err
}

return c, err
}

// CountStyleCategories returns a count of unique categories for pagination.
func CountStyleCategories() (i int, err error) {
err = database.Conn.
Select("COUNT(DISTINCT LOWER(category))").
Table("styles").
Where(notDeleted).
Scan(&i).
Error
if err != nil {
return 0, err
}

return i, nil
}
26 changes: 0 additions & 26 deletions modules/storage/style_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,6 @@ type StyleSearch struct {
ID int `json:"id"`
}

// FindStyleForSearch returns a style for search index or an error.
func FindStyleForSearch(id uint) (StyleSearch, error) {
var res StyleSearch
err := database.Conn.
Table("styles").Select("id, name, description, notes", selectAuthor).
Find(&res, "id = ?", id).Error
if err != nil {
return StyleSearch{}, err
}

return res, nil
}

// FindStylesForSearch queries for styles in batches, and runs a passed action
// that might return an error.
func FindStylesForSearch(action func([]StyleSearch) error) error {
var res []StyleSearch
fn := func(tx *gorm.DB, batch int) error {
return action(res)
}

return database.Conn.
Table("styles").Select("id, name, description, notes", selectAuthor).
Where("deleted_at IS NULL").FindInBatches(&res, 250, fn).Error
}

// TotalSearchStyles returns total amount of userstyles for search page.
func TotalSearchStyles(query, sort string) (int, error) {
q := "SELECT COUNT(*) FROM fts_styles WHERE fts_styles MATCH ?"
Expand Down
27 changes: 27 additions & 0 deletions web/views/style/category.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<section class="ta:c">
<h1>{{ .Title }}</h1>
<p class="fg:3">Browse all available categories.</p>
</section>

<section>
{{ if .Categories }}
<div class="grid flex rwrap mx:r mt:m">
{{ range .Categories }}
<div class="card col gap">
<div class="card-header p:m">
<a href="{{ .URL }}">{{ .Category }}</a>
</div>
<div class="card-footer p:m">
{{ .Count }} userstyle{{ if gt .Count 1 }}s{{ end }}
</div>
</div>
{{ end }}
</div>
{{ else }}
<p class="ta:c">No categories found.</p>
{{ end }}
</section>

{{ if .Pagination.Show }}
{{ template "partials/pagination" .Pagination }}
{{ end }}

0 comments on commit 7d7f114

Please sign in to comment.