diff --git a/api/subreddits_list.go b/api/subreddits_list.go index e20ad98..7a35c09 100644 --- a/api/subreddits_list.go +++ b/api/subreddits_list.go @@ -2,6 +2,8 @@ package api import ( "context" + "strconv" + "strings" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/sqlite/dialect" @@ -11,16 +13,34 @@ import ( ) type ListSubredditsParams struct { - Name string `json:"name"` - Limit int64 `json:"limit"` - Offset int64 `json:"offset"` - OrderBy string `json:"order_by"` - Sort string `json:"sort"` + Q string + All bool + Limit int64 + Offset int64 + OrderBy string + Sort string +} + +func (l *ListSubredditsParams) FillFromQuery(q Queryable) { + l.Q = q.Get("q") + l.All, _ = strconv.ParseBool(q.Get("all")) + l.Limit, _ = strconv.ParseInt(q.Get("limit"), 10, 64) + if l.Limit < 0 { + l.Limit = 25 + } else if l.Limit > 100 { + l.Limit = 100 + } + l.Offset, _ = strconv.ParseInt(q.Get("offset"), 10, 64) + if l.Offset < 0 { + l.Offset = 0 + } + l.OrderBy = q.Get("order_by") + l.Sort = strings.ToLower(q.Get("sort")) } func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) { - if l.Name != "" { - expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Name+"%")) + if l.Q != "" { + expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%")) } if l.Limit > 0 { expr = append(expr, sm.Limit(l.Limit)) @@ -34,14 +54,16 @@ func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) { } else { expr = append(expr, sm.OrderBy(l.OrderBy).Asc()) } + } else { + expr = append(expr, sm.OrderBy(models.SubredditColumns.Name).Asc()) } return expr } func (l ListSubredditsParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) { - if l.Name != "" { - expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Name+"%")) + if l.Q != "" { + expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%")) } return expr @@ -68,3 +90,33 @@ func (api *API) ListSubreddits(ctx context.Context, arg ListSubredditsParams) (r return result, nil } + +// ListSubredditsWithCover returns list of subreddits with cover image. +// +// Image Relationship `R` struct will be nil if there is no cover image. +func (api *API) ListSubredditsWithCover(ctx context.Context, arg ListSubredditsParams) (result ListSubredditsResult, err error) { + ctx, span := tracer.Start(ctx, "api.ListSubredditsWithCover") + defer span.End() + + result, err = api.ListSubreddits(ctx, arg) + if err != nil { + return result, errs.Wrapw(err, "failed to list subreddits with cover") + } + + // Cannot do batch query because we cannot use GROUP BY with ORDER BY in consistent manner. + // + // The problem gets worse when using custom ORDER BY from client. + // + // For consistency, we query images one by one. + // + // Subreddit list is expected to be small, so this should be fine since SQLITE has no network latency. + for _, subreddit := range result.Data { + subreddit.R.Images, err = models.Images.Query(ctx, api.db, + models.SelectWhere.Images.Subreddit.EQ(subreddit.Name), + sm.Limit(1), + sm.OrderBy(models.ImageColumns.CreatedAt).Desc(), + ).All() + } + + return result, nil +} diff --git a/rest/devices/create_phone.http b/rest/devices/create_phone.http new file mode 100644 index 0000000..4fabbad --- /dev/null +++ b/rest/devices/create_phone.http @@ -0,0 +1,15 @@ +POST http://localhost:8080/api/v1/devices HTTP/1.1 +Host: localhost:8080 +Content-Type: application/json + +{ + "name": "Phone", + "slug": "phone", + "resolution_x": 1080, + "resolution_y": 2400, + "nsfw": 1, + "aspect_ratio_tolerance": 0.2, + "enable": 1, + "min_x": 1080, + "min_y": 2400 +} diff --git a/server/routes/page_subreddits.go b/server/routes/page_subreddits.go index 7387cca..7df6208 100644 --- a/server/routes/page_subreddits.go +++ b/server/routes/page_subreddits.go @@ -3,6 +3,8 @@ package routes import ( "net/http" + "github.com/tigorlazuardi/redmage/api" + "github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/log" "github.com/tigorlazuardi/redmage/views" "github.com/tigorlazuardi/redmage/views/subredditsview" @@ -12,12 +14,26 @@ func (routes *Routes) PageSubreddits(rw http.ResponseWriter, r *http.Request) { ctx, span := tracer.Start(r.Context(), "*Routes.PageSubreddits") defer span.End() - data := subredditsview.Data{ - // Subreddits: routes.API.SubredditsList(ctx), - } - c := views.NewContext(routes.Config, r) + var params api.ListSubredditsParams + params.FillFromQuery(r.URL.Query()) + + var data subredditsview.Data + var err error + + data.Subreddits, err = routes.API.ListSubredditsWithCover(ctx, params) + if err != nil { + log.New(ctx).Err(err).Error("failed to list subreddits") + code, message := errs.HTTPMessage(err) + rw.WriteHeader(code) + data.Error = message + if err := subredditsview.Subreddit(c, data).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render subreddits") + } + return + } + if err := subredditsview.Subreddit(c, data).Render(r.Context(), rw); err != nil { log.New(ctx).Err(err).Error("failed to render subreddits view") rw.WriteHeader(http.StatusInternalServerError) diff --git a/server/routes/subreddit_list.go b/server/routes/subreddit_list.go index 5ff7352..2b02acf 100644 --- a/server/routes/subreddit_list.go +++ b/server/routes/subreddit_list.go @@ -26,7 +26,7 @@ func (r *Routes) SubredditsListAPI(rw http.ResponseWriter, req *http.Request) { } func parseSubredditListQuery(req *http.Request) (params api.ListSubredditsParams) { - params.Name = req.FormValue("name") + params.Q = req.FormValue("name") params.Limit, _ = strconv.ParseInt(req.FormValue("limit"), 10, 64) if params.Limit < 1 { params.Limit = 10 diff --git a/views/components/navigation.templ b/views/components/navigation.templ index 7be2b1c..a9b0991 100644 --- a/views/components/navigation.templ +++ b/views/components/navigation.templ @@ -87,7 +87,7 @@ templ Navbar(c *views.Context) { Redmage
-