home: recently added images now separated per subreddit
This commit is contained in:
parent
141abc130e
commit
6eed661f52
|
@ -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
2
.gitignore
vendored
|
@ -30,6 +30,8 @@ public/htmx*.js
|
||||||
db/queries/**/*.go
|
db/queries/**/*.go
|
||||||
*.db
|
*.db
|
||||||
|
|
||||||
|
public/*.min.js
|
||||||
|
|
||||||
models/
|
models/
|
||||||
|
|
||||||
/out
|
/out
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
48
views/homeview/homeview_data.go
Normal file
48
views/homeview/homeview_data.go
Normal 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
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
40
views/utils/relative_schedule_time.templ
Normal file
40
views/utils/relative_schedule_time.templ
Normal 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
17
views/utils/schedule.go
Normal 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())
|
||||||
|
}
|
Loading…
Reference in a new issue