diff --git a/.air.toml b/.air.toml
index f7e4655..a771eff 100644
--- a/.air.toml
+++ b/.air.toml
@@ -7,7 +7,7 @@ args_bin = ["serve"]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
-exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
+exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules", "out"]
exclude_file = []
exclude_regex = [
"_test.go",
diff --git a/.gitignore b/.gitignore
index f46756c..746eff1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@ public/htmx*.js
db/queries/**/*.go
*.db
+public/*.min.js
+
models/
/out
diff --git a/Makefile b/Makefile
index faf6077..272cf19 100644
--- a/Makefile
+++ b/Makefile
@@ -37,6 +37,16 @@ build-dependencies:
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
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
go build -o redmage
@@ -57,4 +67,4 @@ migrate-redo:
@goose redo
migrate-up:
- @goose up
\ No newline at end of file
+ @goose up
diff --git a/server/routes/page_home.go b/server/routes/page_home.go
index 565fc0d..337082a 100644
--- a/server/routes/page_home.go
+++ b/server/routes/page_home.go
@@ -31,7 +31,8 @@ func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
imageListParams := api.ImageListParams{}
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)
if err != nil {
@@ -44,7 +45,7 @@ func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
data := homeview.Data{
SubredditsList: list,
- RecentlyAddedImages: imageList,
+ RecentlyAddedImages: homeview.NewRecentlyAddedImages(imageList.Images),
Error: err,
}
diff --git a/views/components/head.templ b/views/components/head.templ
index 2ddb3e1..ed3f924 100644
--- a/views/components/head.templ
+++ b/views/components/head.templ
@@ -10,6 +10,9 @@ templ Head(vc *views.Context, extras ...templ.Component) {
+
+
+
if vc.Config.Bool("http.hotreload") {
}
@@ -20,5 +23,6 @@ templ Head(vc *views.Context, extras ...templ.Component) {
}
templ HeadTitle(name string) {
-
{name}
-}
\ No newline at end of file
+ { name }
+}
+
diff --git a/views/homeview/homeview.templ b/views/homeview/homeview.templ
index 45a3652..bf0ca73 100644
--- a/views/homeview/homeview.templ
+++ b/views/homeview/homeview.templ
@@ -2,13 +2,8 @@ package homeview
import "github.com/tigorlazuardi/redmage/views/components"
import "github.com/tigorlazuardi/redmage/views"
-import "github.com/tigorlazuardi/redmage/api"
-
-type Data struct {
- SubredditsList api.ListSubredditsResult
- RecentlyAddedImages api.ImageListResult
- Error error
-}
+import "github.com/tigorlazuardi/redmage/views/utils"
+import "time"
templ Home(c *views.Context, data Data) {
@components.Doctype() {
@@ -29,12 +24,18 @@ templ home(_ *views.Context, data Data) {
Recently Added
- @RecentlyAddedImageList(data.RecentlyAddedImages.Images, 0)
+ for _, deviceMapValue := range data.RecentlyAddedImages {
+ { deviceMapValue.device.Name }
+ for _, subreddit := range deviceMapValue.subreddits {
+ { subreddit.subreddit.Name }
+ @RecentlyAddedImageList(subreddit.images, 0)
+ }
+ }
Subreddits
for _, subreddit := range data.SubredditsList.Data {
- { subreddit.Name } - { subreddit.Schedule }
+ { subreddit.Name } - { utils.NextScheduleTime(subreddit.Schedule).Format(time.RubyDate) }
}
diff --git a/views/homeview/homeview_data.go b/views/homeview/homeview_data.go
new file mode 100644
index 0000000..97539e2
--- /dev/null
+++ b/views/homeview/homeview_data.go
@@ -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
+}
diff --git a/views/homeview/recently_added_image.templ b/views/homeview/recently_added_image.templ
index 8165a54..fea232b 100644
--- a/views/homeview/recently_added_image.templ
+++ b/views/homeview/recently_added_image.templ
@@ -3,6 +3,7 @@ package homeview
import "github.com/tigorlazuardi/redmage/models"
import "github.com/tigorlazuardi/redmage/api/reddit"
import "fmt"
+import "github.com/tigorlazuardi/redmage/views/utils"
type ImageCardOption uint
@@ -38,6 +39,9 @@ templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
>{ data.Title }
}
{ data.Poster }
+
+ @utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt)
+
if data.R.Subreddit != nil {
if !opts.Has(HideSubreddit) {
diff --git a/views/utils/relative_schedule_time.templ b/views/utils/relative_schedule_time.templ
new file mode 100644
index 0000000..6aa8613
--- /dev/null
+++ b/views/utils/relative_schedule_time.templ
@@ -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) {
+ { time }
+ @RelativeFromTimeText(id, time)
+}
diff --git a/views/utils/schedule.go b/views/utils/schedule.go
new file mode 100644
index 0000000..5de3cc0
--- /dev/null
+++ b/views/utils/schedule.go
@@ -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())
+}