Add user favorites (#133)

Closes #10

Co-authored-by: video-prize-ranch <cb.8a3w5@simplelogin.co>
Reviewed-on: https://codeberg.org/rimgo/rimgo/pulls/133
Reviewed-by: video-prize-ranch <video-prize-ranch@noreply.codeberg.org>
Co-authored-by: orangix <uleo8b8g@anonaddy.me>
Co-committed-by: orangix <uleo8b8g@anonaddy.me>
This commit is contained in:
orangix 2023-08-29 22:59:59 +00:00 committed by video-prize-ranch
parent 877ee7faa9
commit 0fea1e46a3
6 changed files with 156 additions and 3 deletions

View File

@ -70,7 +70,7 @@ func (client *Client) FetchUser(username string) (User, error) {
}
func (client *Client) FetchSubmissions(username string, sort string, page string) ([]Submission, error) {
cacheData, found := client.Cache.Get(username + "-submissions")
cacheData, found := client.Cache.Get(username + "-submissions-" + sort + page)
if found {
return cacheData.([]Submission), nil
}
@ -98,7 +98,56 @@ func (client *Client) FetchSubmissions(username string, sort string, page string
)
wg.Wait()
client.Cache.Set(username+"-submissions", submissions, 15*time.Minute)
client.Cache.Set(username+"-submissions-"+sort+page, submissions, 15*time.Minute)
return submissions, nil
}
func (client *Client) FetchUserFavorites(username string, sort string, page string) ([]Submission, error) {
cacheData, found := client.Cache.Get(username + "-favorites-" + sort + page)
if found {
return cacheData.([]Submission), nil
}
req, err := http.NewRequest("GET", "https://api.imgur.com/3/account/"+username+"/gallery_favorites/"+page+"/"+sort, nil)
if err != nil {
return []Submission{}, err
}
utils.SetReqHeaders(req)
q := req.URL.Query()
q.Add("client_id", client.ClientID)
req.URL.RawQuery = q.Encode()
res, err := http.DefaultClient.Do(req)
if err != nil {
return []Submission{}, err
}
body, err := io.ReadAll(res.Body)
if err != nil {
return []Submission{}, err
}
data := gjson.Parse(string(body))
submissions := []Submission{}
wg := sync.WaitGroup{}
data.Get("data").ForEach(
func(key, value gjson.Result) bool {
wg.Add(1)
go func() {
defer wg.Done()
submissions = append(submissions, parseSubmission(value))
}()
return true
},
)
wg.Wait()
client.Cache.Set(username+"-favorites-"+sort+page, submissions, 15*time.Minute)
return submissions, nil
}

View File

@ -88,7 +88,7 @@ func main() {
} else {
app.Use("/static", filesystem.New(filesystem.Config{
MaxAge: 2592000,
Root: http.FS(static.GetFiles()),
Root: http.FS(static.GetFiles()),
}))
app.Use(cache.New(cache.Config{
Expiration: 30 * time.Minute,
@ -123,6 +123,7 @@ func main() {
app.Get("/t/:tag/:postID", pages.HandlePost)
app.Get("/r/:sub/:postID", pages.HandlePost)
app.Get("/user/:userID", pages.HandleUser)
app.Get("/user/:userID/favorites", pages.HandleUserFavorites)
app.Get("/user/:userID/comments", pages.HandleUserComments)
app.Get("/user/:userID/cover", pages.HandleUserCover)
app.Get("/user/:userID/avatar", pages.HandleUserAvatar)

View File

@ -91,3 +91,52 @@ func HandleUserComments(c *fiber.Ctx) error {
"comments": comments,
})
}
func HandleUserFavorites(c *fiber.Ctx) error {
utils.SetHeaders(c)
c.Set("X-Frame-Options", "DENY")
c.Set("Cache-Control", "public,max-age=604800")
c.Set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; media-src 'self'; style-src 'unsafe-inline' 'self'; img-src 'self'; manifest-src 'self'; block-all-mixed-content")
page := "0"
if c.Query("page") != "" {
page = c.Query("page")
}
pageNumber, err := strconv.Atoi(c.Query("page"))
if err != nil {
pageNumber = 0
}
user, err := ApiClient.FetchUser(c.Params("userID"))
if err != nil && err.Error() == "ratelimited by imgur" {
return c.Status(429).Render("errors/429", fiber.Map{
"path": c.Path(),
})
}
if err != nil {
return err
}
if user.Username == "" {
return c.Status(404).Render("errors/404", nil)
}
favorites, err := ApiClient.FetchUserFavorites(c.Params("userID"), "newest", page)
if err != nil && err.Error() == "ratelimited by imgur" {
c.Status(429)
return c.Render("errors/429", fiber.Map{
"path": c.Path(),
})
}
if err != nil {
return err
}
return c.Render("userFavorites", fiber.Map{
"user": user,
"favorites": favorites,
"page": page,
"nextPage": pageNumber + 1,
"prevPage": pageNumber - 1,
})
}

View File

@ -24,6 +24,7 @@
<hr class="sm:border-0 flex-grow">
<div class="flex flex-col items-center sm:items-end">
<a href="/user/{{user.Username}}"><b>Submissions</b></a>
<a href="/user/{{user.Username}}/favorites">Favorites</a>
<a href="/user/{{user.Username}}/comments">Comments</a>
</div>
</div>

View File

@ -24,6 +24,7 @@
<hr class="sm:border-0 flex-grow">
<div class="flex flex-col items-center sm:items-end">
<a href="/user/{{user.Username}}">Submissions</a>
<a href="/user/{{user.Username}}/favorites">Favorites</a>
<a href="/user/{{user.Username}}/comments"><b>Comments</b></a>
</div>
</div>

52
views/userFavorites.hbs Normal file
View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{user.Username}}'s favorites - rimgo</title>
{{> partials/head }}
</head>
<body class="font-sans text-lg bg-slate-800 text-white">
{{> partials/nav }}
<section class="my-4 w-full flex flex-col items-center">
{{> partials/searchBar }}
</section>
<header class="p-4 rounded-xl text-white mb-4" style="background-image: url('{{user.Cover}}');">
<div class="flex flex-col sm:flex-row items-center gap-2">
<img class="rounded-full" src="{{user.Avatar}}" width="72" height="72">
<div class="items-center sm:items-start text-center sm:text-left">
<h2 class="font-bold text-2xl">{{user.Username}}</h2>
<p>{{user.Points}} pts · {{user.CreatedAt}}</p>
</div>
<hr class="sm:border-0 flex-grow">
<div class="flex flex-col items-center sm:items-end">
<a href="/user/{{user.Username}}">Submissions</a>
<a href="/user/{{user.Username}}/favorites"><b>Favorites</b></a>
<a href="/user/{{user.Username}}/comments">Comments</a>
</div>
</div>
<p class="mt-2">{{user.Bio}}</p>
</header>
<main>
<div class="posts">
{{#each favorites}}
{{> partials/post }}
{{/each}}
</div>
<div class="flex mt-4 font-bold justify-between">
{{#noteq page "0" }}
<a href="{{channel.RelUrl}}?page={{prevPage}}">Previous page</a>
{{/noteq}}
<a href="{{channel.RelUrl}}?page={{nextPage}}">Next page</a>
</div>
</main>
{{> partials/footer }}
</body>
</html>