home: recently added images now separated per subreddit

This commit is contained in:
Tigor Hutasuhut 2024-04-28 22:00:56 +07:00
parent 141abc130e
commit 6eed661f52
10 changed files with 142 additions and 15 deletions

View file

@ -7,7 +7,7 @@ args_bin = ["serve"]
bin = "./tmp/main" bin = "./tmp/main"
cmd = "go build -o ./tmp/main ." cmd = "go build -o ./tmp/main ."
delay = 1000 delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"] exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules", "out"]
exclude_file = [] exclude_file = []
exclude_regex = [ exclude_regex = [
"_test.go", "_test.go",

2
.gitignore vendored
View file

@ -30,6 +30,8 @@ public/htmx*.js
db/queries/**/*.go db/queries/**/*.go
*.db *.db
public/*.min.js
models/ models/
/out /out

View file

@ -37,6 +37,16 @@ build-dependencies:
echo "Htmx response targets not found, installing it" echo "Htmx response targets not found, installing it"
curl -o public/htmx-response-targets-1.9.11.min.js https://cdnjs.cloudflare.com/ajax/libs/htmx/1.9.11/ext/response-targets.min.js curl -o public/htmx-response-targets-1.9.11.min.js https://cdnjs.cloudflare.com/ajax/libs/htmx/1.9.11/ext/response-targets.min.js
fi fi
@if [ ! -f "public/dayjs-1.11.10.min.js" ]; then
mkdir -p public
echo "Dayjs not found, installing it"
curl -o public/dayjs-1.11.10.min.js https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/dayjs.min.js
fi
@if [ ! -f "public/dayjs-relativeTime-1.11.10.min.js" ]; then
mkdir -p public
echo "Dayjs Relative Time not found, installing it"
curl -o public/dayjs-relativeTime-1.11.10.min.js https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/plugin/relativeTime.min.js
fi
build: build-dependencies prepare build: build-dependencies prepare
go build -o redmage go build -o redmage
@ -57,4 +67,4 @@ migrate-redo:
@goose redo @goose redo
migrate-up: migrate-up:
@goose up @goose up

View file

@ -31,7 +31,8 @@ func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
imageListParams := api.ImageListParams{} imageListParams := api.ImageListParams{}
imageListParams.FillFromQuery(r.URL.Query()) imageListParams.FillFromQuery(r.URL.Query())
imageListParams.CreatedAt = time.Now().Add(-time.Hour * 24 * 3) // images in the last 3 days. imageListParams.CreatedAt = time.Now().Add(-time.Hour * 24) // images in the last 24 hours
imageListParams.Limit = 0
imageList, err := routes.API.ImagesListWithDevicesAndSubreddits(ctx, imageListParams) imageList, err := routes.API.ImagesListWithDevicesAndSubreddits(ctx, imageListParams)
if err != nil { if err != nil {
@ -44,7 +45,7 @@ func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
data := homeview.Data{ data := homeview.Data{
SubredditsList: list, SubredditsList: list,
RecentlyAddedImages: imageList, RecentlyAddedImages: homeview.NewRecentlyAddedImages(imageList.Images),
Error: err, Error: err,
} }

View file

@ -10,6 +10,9 @@ templ Head(vc *views.Context, extras ...templ.Component) {
<link rel="icon" href="/public/favicon.svg"/> <link rel="icon" href="/public/favicon.svg"/>
<script src="/public/htmx-1.9.11.min.js"></script> <script src="/public/htmx-1.9.11.min.js"></script>
<script src="/public/htmx-response-targets-1.9.11.min.js"></script> <script src="/public/htmx-response-targets-1.9.11.min.js"></script>
<script src="/public/dayjs-1.11.10.min.js"></script>
<script src="/public/dayjs-relativeTime-1.11.10.min.js"></script>
<script>dayjs.extend(window.dayjs_plugin_relativeTime)</script>
if vc.Config.Bool("http.hotreload") { if vc.Config.Bool("http.hotreload") {
<script src="/public/hot_reload.js"></script> <script src="/public/hot_reload.js"></script>
} }
@ -20,5 +23,6 @@ templ Head(vc *views.Context, extras ...templ.Component) {
} }
templ HeadTitle(name string) { templ HeadTitle(name string) {
<title>{name}</title> <title>{ name }</title>
} }

View file

@ -2,13 +2,8 @@ package homeview
import "github.com/tigorlazuardi/redmage/views/components" import "github.com/tigorlazuardi/redmage/views/components"
import "github.com/tigorlazuardi/redmage/views" import "github.com/tigorlazuardi/redmage/views"
import "github.com/tigorlazuardi/redmage/api" import "github.com/tigorlazuardi/redmage/views/utils"
import "time"
type Data struct {
SubredditsList api.ListSubredditsResult
RecentlyAddedImages api.ImageListResult
Error error
}
templ Home(c *views.Context, data Data) { templ Home(c *views.Context, data Data) {
@components.Doctype() { @components.Doctype() {
@ -29,12 +24,18 @@ templ home(_ *views.Context, data Data) {
<div class="prose"> <div class="prose">
<section class="mb-4 mx-auto"> <section class="mb-4 mx-auto">
<h1>Recently Added</h1> <h1>Recently Added</h1>
@RecentlyAddedImageList(data.RecentlyAddedImages.Images, 0) for _, deviceMapValue := range data.RecentlyAddedImages {
<h2>{ deviceMapValue.device.Name }</h2>
for _, subreddit := range deviceMapValue.subreddits {
<h4>{ subreddit.subreddit.Name }</h4>
@RecentlyAddedImageList(subreddit.images, 0)
}
}
</section> </section>
<section> <section>
<h1>Subreddits</h1> <h1>Subreddits</h1>
for _, subreddit := range data.SubredditsList.Data { for _, subreddit := range data.SubredditsList.Data {
<h3>{ subreddit.Name } - { subreddit.Schedule }</h3> <h3>{ subreddit.Name } - { utils.NextScheduleTime(subreddit.Schedule).Format(time.RubyDate) }</h3>
} }
</section> </section>
</div> </div>

View file

@ -0,0 +1,48 @@
package homeview
import (
"github.com/tigorlazuardi/redmage/api"
"github.com/tigorlazuardi/redmage/models"
)
type Data struct {
SubredditsList api.ListSubredditsResult
RecentlyAddedImages RecentlyAddedImages
Error error
}
type RecentlyAddedImages = map[int32]deviceMapValue
type deviceMapValue struct {
device *models.Device
subreddits map[int32]subredditMapValue
}
type subredditMapValue struct {
subreddit *models.Subreddit
images []*models.Image
}
func NewRecentlyAddedImages(images models.ImageSlice) RecentlyAddedImages {
r := make(RecentlyAddedImages)
for _, image := range images {
if image.R.Device == nil || image.R.Subreddit == nil {
continue
}
if _, ok := r[image.R.Device.ID]; !ok {
r[image.R.Device.ID] = deviceMapValue{
device: image.R.Device,
subreddits: make(map[int32]subredditMapValue),
}
}
if _, ok := r[image.R.Device.ID].subreddits[image.R.Subreddit.ID]; !ok {
r[image.R.Device.ID].subreddits[image.R.Subreddit.ID] = subredditMapValue{}
}
images := append(r[image.R.Device.ID].subreddits[image.R.Subreddit.ID].images, image)
r[image.R.Device.ID].subreddits[image.R.Subreddit.ID] = subredditMapValue{
subreddit: image.R.Subreddit,
images: images,
}
}
return r
}

View file

@ -3,6 +3,7 @@ package homeview
import "github.com/tigorlazuardi/redmage/models" import "github.com/tigorlazuardi/redmage/models"
import "github.com/tigorlazuardi/redmage/api/reddit" import "github.com/tigorlazuardi/redmage/api/reddit"
import "fmt" import "fmt"
import "github.com/tigorlazuardi/redmage/views/utils"
type ImageCardOption uint type ImageCardOption uint
@ -38,6 +39,9 @@ templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
>{ data.Title }</a> >{ data.Title }</a>
} }
<a class="text-primary-content underline" href={ templ.URL(data.PosterURL) }>{ data.Poster }</a> <a class="text-primary-content underline" href={ templ.URL(data.PosterURL) }>{ data.Poster }</a>
<div>
@utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt)
</div>
<div class="card-actions justify-between"> <div class="card-actions justify-between">
if data.R.Subreddit != nil { if data.R.Subreddit != nil {
if !opts.Has(HideSubreddit) { if !opts.Has(HideSubreddit) {

View file

@ -0,0 +1,40 @@
package utils
import "strings"
// RelativeTimeText updates the text content of the element to be a relative time text.
//
// Every second it updates the text content to be the relative time text of the input string.
script RelativeFromTimeText(id string, time string) {
const el = document.getElementById(id)
const timeText = dayjs(time).fromNow()
el.textContent = timeText
const interval = setInterval(() => {
const timeText = dayjs(time).fromNow()
el.textContent = timeText
}, 1000)
const obs = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const removed of mutation.removedNodes) {
if (el === removed) {
clearInterval(interval)
obs.disconnect()
}
}
}
})
obs.observe(el.parentNode, { childList: true })
}
templ RelativeTimeNode(id string, time string, class ...string) {
<span
id={ id }
class={ strings.Join(class, " ") }
>{ time }</span>
@RelativeFromTimeText(id, time)
}

17
views/utils/schedule.go Normal file
View file

@ -0,0 +1,17 @@
package utils
import (
"time"
"github.com/robfig/cron/v3"
)
var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
func NextScheduleTime(in string) (out time.Time) {
s, err := cronParser.Parse(in)
if err != nil {
return time.Time{}
}
return s.Next(time.Now())
}