6 Commits

Author SHA1 Message Date
video-prize-ranch
5438365e50 Fix previous/next page buttons 2023-08-29 18:58:11 -04:00
orangix
3d707c561e cache per page 2023-08-29 23:58:30 +02:00
orangix
9fd38bed03 added user favorites 2023-08-20 07:58:38 +02:00
video-prize-ranch
877ee7faa9 Change max age on cache-control in production 2023-08-16 11:46:41 -04:00
video-prize-ranch
d490581c44 Fix overflowing text on mobile 2023-08-16 11:07:32 -04:00
orangix
730aead3a1 fix header on user submissions 2023-08-16 10:26:35 -04:00
9 changed files with 164 additions and 11 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) { 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 { if found {
return cacheData.([]Submission), nil return cacheData.([]Submission), nil
} }
@@ -98,7 +98,56 @@ func (client *Client) FetchSubmissions(username string, sort string, page string
) )
wg.Wait() 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 return submissions, nil
} }

View File

@@ -72,8 +72,7 @@ func main() {
if os.Getenv("ENV") == "dev" { if os.Getenv("ENV") == "dev" {
app.Use("/static", filesystem.New(filesystem.Config{ app.Use("/static", filesystem.New(filesystem.Config{
MaxAge: 2592000, Root: http.Dir("./static"),
Root: http.Dir("./static"),
})) }))
app.Get("/errors/429", func(c *fiber.Ctx) error { app.Get("/errors/429", func(c *fiber.Ctx) error {
return c.Render("errors/429", nil) return c.Render("errors/429", nil)
@@ -88,7 +87,8 @@ func main() {
}) })
} else { } else {
app.Use("/static", filesystem.New(filesystem.Config{ app.Use("/static", filesystem.New(filesystem.Config{
Root: http.FS(static.GetFiles()), MaxAge: 2592000,
Root: http.FS(static.GetFiles()),
})) }))
app.Use(cache.New(cache.Config{ app.Use(cache.New(cache.Config{
Expiration: 30 * time.Minute, Expiration: 30 * time.Minute,
@@ -123,6 +123,7 @@ func main() {
app.Get("/t/:tag/:postID", pages.HandlePost) app.Get("/t/:tag/:postID", pages.HandlePost)
app.Get("/r/:sub/:postID", pages.HandlePost) app.Get("/r/:sub/:postID", pages.HandlePost)
app.Get("/user/:userID", pages.HandleUser) app.Get("/user/:userID", pages.HandleUser)
app.Get("/user/:userID/favorites", pages.HandleUserFavorites)
app.Get("/user/:userID/comments", pages.HandleUserComments) app.Get("/user/:userID/comments", pages.HandleUserComments)
app.Get("/user/:userID/cover", pages.HandleUserCover) app.Get("/user/:userID/cover", pages.HandleUserCover)
app.Get("/user/:userID/avatar", pages.HandleUserAvatar) app.Get("/user/:userID/avatar", pages.HandleUserAvatar)

View File

@@ -91,3 +91,52 @@ func HandleUserComments(c *fiber.Ctx) error {
"comments": comments, "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

@@ -7,7 +7,7 @@ body {
} }
p a { p a {
text-decoration: underline @apply break-words underline
} }
.posts { .posts {

View File

@@ -3,7 +3,7 @@
<img class="object-cover block w-full h-[300px] sm:w-[120px] sm:h-[140px] rounded-lg rounded-b-none sm:rounded-b-lg" src="{{this.Post.Cover.Url}}" alt=""> <img class="object-cover block w-full h-[300px] sm:w-[120px] sm:h-[140px] rounded-lg rounded-b-none sm:rounded-b-lg" src="{{this.Post.Cover.Url}}" alt="">
<div class="flex flex-col gap-2 bg-slate-600 p-4 rounded-lg rounded-t-none sm:rounded-t-lg w-full"> <div class="flex flex-col gap-2 bg-slate-600 p-4 rounded-lg rounded-t-none sm:rounded-t-lg w-full">
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<p>{{{this.Comment}}}</p> <p class="md-container">{{{this.Comment}}}</p>
<div class="flex-grow"></div> <div class="flex-grow"></div>
<div class="flex gap-2"> <div class="flex gap-2">
<span title="{{this.CreatedAt}}">{{this.RelTime}}</span> <span title="{{this.CreatedAt}}">{{this.RelTime}}</span>

View File

@@ -25,7 +25,7 @@
</header> </header>
<main> <main>
<div class="flex gap-4 items-center my-4"> <div class="flex flex-col gap-2 md:flex-row md:gap-4 md:items-center my-4">
{{#if post.User.Username}} {{#if post.User.Username}}
<a href="/user/{{post.User.Username}}" class="flex gap-2 items-center"> <a href="/user/{{post.User.Username}}" class="flex gap-2 items-center">
<img src="{{post.User.Avatar}}" class="rounded-full" width="36" height="36" /> <img src="{{post.User.Avatar}}" class="rounded-full" width="36" height="36" />

View File

@@ -15,15 +15,16 @@
</section> </section>
<header class="p-4 rounded-xl text-white mb-4" style="background-image: url('{{user.Cover}}');"> <header class="p-4 rounded-xl text-white mb-4" style="background-image: url('{{user.Cover}}');">
<div class="flex items-center gap-2"> <div class="flex flex-col sm:flex-row items-center gap-2">
<img class="rounded-full" src="{{user.Avatar}}" width="72" height="72"> <img class="rounded-full" src="{{user.Avatar}}" width="72" height="72">
<div> <div class="items-center sm:items-start text-center sm:text-left">
<h2 class="font-bold text-2xl">{{user.Username}}</h2> <h2 class="font-bold text-2xl">{{user.Username}}</h2>
<p>{{user.Points}} pts · {{user.CreatedAt}}</p> <p>{{user.Points}} pts · {{user.CreatedAt}}</p>
</div> </div>
<hr class="sm:border-0 flex-grow"> <hr class="sm:border-0 flex-grow">
<div class="flex flex-col sm:items-end"> <div class="flex flex-col items-center sm:items-end">
<a href="/user/{{user.Username}}"><b>Submissions</b></a> <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> <a href="/user/{{user.Username}}/comments">Comments</a>
</div> </div>
</div> </div>

View File

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