next button for tagged posts (#169)

No previous button because that would be annoying to implement serverside with pagination etc.

Closes #115

Fixed conflict with #154, #155, #156

Co-authored-by: video-prize-ranch <cb.8a3w5@simplelogin.co>
Reviewed-on: https://codeberg.org/rimgo/rimgo/pulls/169
Reviewed-by: video-prize-ranch <video-prize-ranch@noreply.codeberg.org>
Co-authored-by: orangix <orangix@noreply.codeberg.org>
Co-committed-by: orangix <orangix@noreply.codeberg.org>
This commit is contained in:
orangix 2024-02-05 22:47:04 +00:00 committed by video-prize-ranch
parent 7433265991
commit 927ea20fad
3 changed files with 98 additions and 60 deletions

View File

@ -3,9 +3,9 @@ package api
import ( import (
"io" "io"
"net/http" "net/http"
"net/url"
"strings" "strings"
"sync"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@ -21,6 +21,9 @@ type Tag struct {
} }
func (client *Client) FetchTag(tag string, sort string, page string) (Tag, error) { func (client *Client) FetchTag(tag string, sort string, page string) (Tag, error) {
// Dots are automatically removed on Imgur, so more cache hits
tag = strings.ReplaceAll(tag, ".", "")
cacheData, found := client.Cache.Get(tag + sort + page + "-tag") cacheData, found := client.Cache.Get(tag + sort + page + "-tag")
if found { if found {
return cacheData.(Tag), nil return cacheData.(Tag), nil
@ -64,47 +67,44 @@ func (client *Client) FetchTag(tag string, sort string, page string) (Tag, error
data := gjson.Parse(string(body)) data := gjson.Parse(string(body))
wg := sync.WaitGroup{}
posts := make([]Submission, 0) posts := make([]Submission, 0)
data.Get("posts").ForEach( data.Get("posts").ForEach(
func(key, value gjson.Result) bool { func(key, value gjson.Result) bool {
wg.Add(1) url, _ := url.Parse(strings.ReplaceAll(value.Get("url").String(), "https://imgur.com", ""))
q := url.Query()
q.Add("tag", tag+"."+sort+"."+page+"."+key.String())
url.RawQuery = q.Encode()
go func() { posts = append(posts, Submission{
defer wg.Done() Id: value.Get("id").String(),
posts = append(posts, Submission{ Title: value.Get("title").String(),
Id: value.Get("id").String(), Link: url.String(),
Title: value.Get("title").String(), Cover: Media{
Link: strings.ReplaceAll(value.Get("url").String(), "https://imgur.com", ""), Id: value.Get("cover_id").String(),
Cover: Media{ Type: value.Get("cover.type").String(),
Id: value.Get("cover_id").String(), Url: strings.ReplaceAll(value.Get("cover.url").String(), "https://i.imgur.com", ""),
Type: value.Get("cover.type").String(), },
Url: strings.ReplaceAll(value.Get("cover.url").String(), "https://i.imgur.com", ""), Points: value.Get("point_count").Int(),
}, Upvotes: value.Get("upvote_count").Int(),
Points: value.Get("point_count").Int(), Downvotes: value.Get("downvote_count").Int(),
Upvotes: value.Get("upvote_count").Int(), Comments: value.Get("comment_count").Int(),
Downvotes: value.Get("downvote_count").Int(), Views: value.Get("view_count").Int(),
Comments: value.Get("comment_count").Int(), IsAlbum: value.Get("is_album").Bool(),
Views: value.Get("view_count").Int(), })
IsAlbum: value.Get("is_album").Bool(),
})
}()
return true return true
}, },
) )
wg.Wait()
tagData := Tag{ tagData := Tag{
Tag: tag, Tag: tag,
Display: data.Get("display").String(), Display: data.Get("display").String(),
Sort: sort, Sort: sort,
PostCount: data.Get("post_count").Int(), PostCount: data.Get("post_count").Int(),
Posts: posts, Posts: posts,
Background: "/" + data.Get("background_id").String() + ".webp", Background: "/" + data.Get("background_id").String() + ".webp",
} }
client.Cache.Set(tag + sort + page + "-tag", tagData, cache.DefaultExpiration) client.Cache.Set(tag+sort+page+"-tag", tagData, 4*cache.DefaultExpiration)
return tagData, nil return tagData, nil
} }

View File

@ -3,6 +3,7 @@ package pages
import ( import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"strconv"
"strings" "strings"
"codeberg.org/rimgo/rimgo/api" "codeberg.org/rimgo/rimgo/api"
@ -10,6 +11,27 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
// Cursed function
func nextInTag(client *api.Client, tagname, sort, page, I string) string {
i, err := strconv.Atoi(I)
if err != nil || i < 0 {
return ""
}
tag, err := client.FetchTag(tagname, sort, page)
if err != nil {
return ""
}
if i >= len(tag.Posts)-1 {
pageNumber, _ := strconv.Atoi(page)
tagn, err := client.FetchTag(tagname, sort, strconv.Itoa(pageNumber+1))
if err != nil {
return ""
}
return tagn.Posts[0].Link
}
return tag.Posts[i+1].Link
}
func HandlePost(c *fiber.Ctx) error { func HandlePost(c *fiber.Ctx) error {
utils.SetHeaders(c) utils.SetHeaders(c)
c.Set("X-Frame-Options", "DENY") c.Set("X-Frame-Options", "DENY")
@ -56,8 +78,16 @@ func HandlePost(c *fiber.Ctx) error {
} }
c.Set("Content-Security-Policy", csp) c.Set("Content-Security-Policy", csp)
var next string
tagParam := strings.Split(c.Query("tag"), ".")
if len(tagParam) == 4 {
tag, sort, page, index := tagParam[0], tagParam[1], tagParam[2], tagParam[3]
next = nextInTag(ApiClient, tag, sort, page, index)
}
return c.Render("post", fiber.Map{ return c.Render("post", fiber.Map{
"post": post, "post": post,
"next": next,
"comments": comments, "comments": comments,
"nonce": nonce, "nonce": nonce,
}) })

View File

@ -4,7 +4,7 @@
<head> <head>
<title> <title>
{{#if post.Title}} {{#if post.Title}}
{{post.Title}} - {{post.Title}} -
{{/if}} {{/if}}
rimgo rimgo
</title> </title>
@ -23,33 +23,40 @@
<h1 class="text-3xl font-bold">{{post.Title}}</h1> <h1 class="text-3xl font-bold">{{post.Title}}</h1>
<p>{{post.CreatedAt}}</p> <p>{{post.CreatedAt}}</p>
</header> </header>
<main> <main>
<div class="flex flex-col gap-2 md:flex-row md:gap-4 md:items-center my-4"> <div class="flex flex-col sm:flex-row my-4 w-full justify-between">
{{#if post.User.Username}} <div class="flex flex-col gap-2 md:flex-row md:gap-4 md:items-center">
<a href="/user/{{post.User.Username}}" class="flex gap-2 items-center"> {{#if post.User.Username}}
<img src="{{post.User.Avatar}}" class="rounded-full" width="36" height="36" /> <a href="/user/{{post.User.Username}}" class="flex gap-2 items-center">
<p> <img src="{{post.User.Avatar}}" class="rounded-full" width="36" height="36" />
<b>{{post.User.Username}}</b> <p>
</p> <b>{{post.User.Username}}</b>
</a> </p>
{{/if}} </a>
<div class="flex gap-2 items-center">
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhEye.svg" alt="Views" width="24px" height="24px">
<p>{{post.Views}}</p>
</div>
{{#if post.SharedWithCommunity}}
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhArrowFatUp.svg" alt="Likes" width="24px" height="24px">
<p>{{post.Upvotes}}</p>
</div>
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhArrowFatDown.svg" alt="Dislikes" width="24px" height="24px">
<p>{{post.Downvotes}}</p>
</div>
{{/if}} {{/if}}
<div class="flex gap-2 items-center">
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhEye.svg" alt="Views" width="24px" height="24px">
<p>{{post.Views}}</p>
</div>
{{#if post.SharedWithCommunity}}
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhArrowFatUp.svg" alt="Likes" width="24px" height="24px">
<p>{{post.Upvotes}}</p>
</div>
<div class="flex flex-center gap-2">
<img class="icon invert" src="/static/icons/PhArrowFatDown.svg" alt="Dislikes" width="24px" height="24px">
<p>{{post.Downvotes}}</p>
</div>
{{/if}}
</div>
</div> </div>
{{#noteq next ""}}
<a href="{{next}}" class="self-end">
<button class="p-2 rounded-lg bg-slate-600">Next &gt;</button>
</a>
{{/noteq}}
</div> </div>
<div class="flex flex-center flex-col break-words"> <div class="flex flex-center flex-col break-words">
@ -70,7 +77,7 @@
{{#if this.Description}} {{#if this.Description}}
<p>{{{this.Description}}}</p> <p>{{{this.Description}}}</p>
{{/if}} {{/if}}
{{/each}} {{/each}}
</div> </div>
{{#if post.tags}} {{#if post.tags}}
@ -78,7 +85,7 @@
<style nonce="{{nonce}}"> <style nonce="{{nonce}}">
{{#each post.tags}} {{#each post.tags}}
.{{this.BackgroundId}} { background-image: url('{{this.Background}}') } .{{this.BackgroundId}} { background-image: url('{{this.Background}}') }
{{/each}} {{/each}}
</style> </style>
{{#each post.tags}} {{#each post.tags}}
<a href="/t/{{this.Tag}}"> <a href="/t/{{this.Tag}}">
@ -101,7 +108,8 @@
{{#if comments}} {{#if comments}}
<div> <div>
<input id="comments__expandBtn" type="checkbox" checked> <input id="comments__expandBtn" type="checkbox" checked>
<label class="comments__expandBtn__label my-2 py-4 border-solid border-t-2 border-slate-400" for="comments__expandBtn"> <label class="comments__expandBtn__label my-2 py-4 border-solid border-t-2 border-slate-400"
for="comments__expandBtn">
<h3 class="text-xl font-bold">Comments ({{post.Comments}})</h3> <h3 class="text-xl font-bold">Comments ({{post.Comments}})</h3>
<span class="text-xl font-bold"></span> <span class="text-xl font-bold"></span>
</label> </label>