subreddit-add: enhanced subreddit check and add form

This commit is contained in:
Tigor Hutasuhut 2024-05-03 10:27:03 +07:00
parent 15f9634187
commit 4e7f91d098
6 changed files with 166 additions and 69 deletions

View file

@ -39,7 +39,18 @@ func (reddit *Reddit) CheckSubreddit(ctx context.Context, params CheckSubredditP
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound { if resp.StatusCode == http.StatusNotFound {
return actual, errs.Wrapw(err, "user or subreddit not found", "url", url, "params", params).Code(http.StatusNotFound) var msg string
if params.SubredditType == SubredditTypeUser {
msg = "user not found"
}
if params.SubredditType == SubredditTypeSub {
msg = "subreddit not found"
}
return actual, errs.Wrapw(err, msg, "url", url, "params", params).Code(http.StatusNotFound)
}
if params.SubredditType == SubredditTypeUser && resp.StatusCode == http.StatusOK {
return params.Subreddit, nil
} }
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {

View file

@ -4,6 +4,8 @@ import (
"context" "context"
"github.com/tigorlazuardi/redmage/api/reddit" "github.com/tigorlazuardi/redmage/api/reddit"
"github.com/tigorlazuardi/redmage/models"
"github.com/tigorlazuardi/redmage/pkg/errs"
) )
type SubredditCheckParam = reddit.CheckSubredditParams type SubredditCheckParam = reddit.CheckSubredditParams
@ -14,3 +16,18 @@ func (api *API) SubredditCheck(ctx context.Context, params SubredditCheckParam)
return api.reddit.CheckSubreddit(ctx, params) return api.reddit.CheckSubreddit(ctx, params)
} }
func (api *API) SubredditRegistered(ctx context.Context, params SubredditCheckParam) (registered bool, err error) {
ctx, span := tracer.Start(ctx, "*API.SubredditRegistered")
defer span.End()
registered, err = models.Subreddits.Query(ctx, api.db,
models.SelectWhere.Subreddits.Name.EQ(params.Subreddit),
models.SelectWhere.Subreddits.Subtype.EQ(int32(params.SubredditType)),
).Exists()
if err != nil {
return registered, errs.Wrapw(err, "failed to check subreddit registered state", "params", params)
}
return registered, err
}

View file

@ -52,6 +52,11 @@ func (routes *Routes) SubredditCheckHTMX(rw http.ResponseWriter, r *http.Request
name := r.FormValue("name") name := r.FormValue("name")
data.Value = name data.Value = name
var subtype reddit.SubredditType
_ = subtype.Parse(r.FormValue("type"))
data.Type = subtype
if name == "" { if name == "" {
if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil { if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil {
log.New(r.Context()).Err(err).Error("failed to render subreddit input form") log.New(r.Context()).Err(err).Error("failed to render subreddit input form")
@ -62,12 +67,9 @@ func (routes *Routes) SubredditCheckHTMX(rw http.ResponseWriter, r *http.Request
ctx, span := tracer.Start(r.Context(), "*Routes.SubredditCheckHTMX") ctx, span := tracer.Start(r.Context(), "*Routes.SubredditCheckHTMX")
defer span.End() defer span.End()
var t reddit.SubredditType
_ = t.Parse(r.FormValue("type"))
params := api.SubredditCheckParam{ params := api.SubredditCheckParam{
Subreddit: name, Subreddit: name,
SubredditType: t, SubredditType: subtype,
} }
actual, err := routes.API.SubredditCheck(ctx, params) actual, err := routes.API.SubredditCheck(ctx, params)
@ -81,7 +83,30 @@ func (routes *Routes) SubredditCheckHTMX(rw http.ResponseWriter, r *http.Request
} }
return return
} }
params.Subreddit = actual
data.Value = actual data.Value = actual
exist, err := routes.API.SubredditRegistered(ctx, params)
if err != nil {
log.New(ctx).Err(err).Error("failed to check subreddit")
code, message := errs.HTTPMessage(err)
rw.WriteHeader(code)
data.Error = message
if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil {
log.New(r.Context()).Err(err).Error("failed to render subreddit input form")
}
return
}
if exist {
rw.WriteHeader(http.StatusNotFound)
data.Error = "subreddit already registered"
if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil {
log.New(r.Context()).Err(err).Error("failed to render subreddit input form")
}
return
}
data.Valid = true data.Valid = true
if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil { if err := addview.SubredditInputForm(data).Render(r.Context(), rw); err != nil {

View file

@ -2,7 +2,6 @@ package addview
import "github.com/tigorlazuardi/redmage/views" import "github.com/tigorlazuardi/redmage/views"
import "github.com/tigorlazuardi/redmage/views/components" import "github.com/tigorlazuardi/redmage/views/components"
import "github.com/tigorlazuardi/redmage/views/utils"
templ Addview(c *views.Context) { templ Addview(c *views.Context) {
@components.Doctype() { @components.Doctype() {
@ -19,73 +18,18 @@ templ AddviewContent(c *views.Context) {
@components.Container() { @components.Container() {
<h1>Add Subreddit</h1> <h1>Add Subreddit</h1>
<div class="divider"></div> <div class="divider"></div>
<form hx-post="/htmx/subreddit/add" class="grid grid-cols-1 sm:grid-cols-2 gap-4"> <form
onkeydown="return event.key !== 'Enter'"
hx-post="/htmx/subreddit/add"
class="grid grid-cols-1 sm:grid-cols-2 gap-4"
>
<label id="subreddit-input" class="form-control w-full"> <label id="subreddit-input" class="form-control w-full">
@SubredditInputForm(SubredditInputData{}) @SubredditInputForm(SubredditInputData{})
</label> </label>
<label id="subreddit-type-input" class="form-control w-full">
@SubredditTypeInput(SubredditTypeData{})
</label>
</form> </form>
} }
</main> </main>
} }
type SubredditInputData struct {
Value string
Error string
Valid bool
}
templ SubredditInputForm(data SubredditInputData) {
<div class="label">
<span
class={ utils.CX(map[string]bool{
"label-text": true,
"text-error": data.Error != "",
"text-success": data.Valid,
"text-base": true,
}) }
>Subreddit Name</span>
</div>
@subredditInputField("/htmx/subreddits/check", data)
<div class="label">
<span
class={ utils.CX(map[string]bool{
"label-text": true,
"text-error": data.Error != "",
"text-success": data.Valid,
"min-h-[1rem]": true,
}) }
>
if data.Valid {
Subreddit / User target is valid
} else {
{ data.Error }
}
</span>
</div>
}
templ subredditInputField(target string, data SubredditInputData) {
<input
type="text"
id="name"
name="name"
hx-post={ target }
hx-target="#subreddit-input"
hx-target-4xx="#subreddit-input"
hx-trigger="keyup changed delay:1s, on-demand"
hx-target-5x={ components.NotificationContainerID }
value={ data.Value }
placeholder="e.g. 'wallpaper' or 'EarthPorn'"
class={ utils.CX(map[string]bool{
"input": true,
"input-bordered": true,
"input-error": data.Error != "",
"text-error": data.Error != "",
"input-success": data.Valid,
"text-success": data.Valid,
}) }
required
data-error={ data.Error }
hx-on::load="this.setCustomValidity(this.getAttribute('data-error'))"
/>
}

View file

@ -0,0 +1,73 @@
package addview
import "github.com/tigorlazuardi/redmage/views/components"
import "github.com/tigorlazuardi/redmage/views/utils"
import "github.com/tigorlazuardi/redmage/api/reddit"
type SubredditInputData struct {
Value string
Error string
Valid bool
Type reddit.SubredditType
}
templ SubredditInputForm(data SubredditInputData) {
<div class="label">
<span
class={ utils.CX(map[string]bool{
"label-text": true,
"text-error": data.Error != "",
"text-success": data.Valid,
"text-base": true,
}) }
>Subreddit Name</span>
</div>
@subredditInputField("/htmx/subreddits/check", data)
<div class="label">
<span
class={ utils.CX(map[string]bool{
"label-text": true,
"text-error": data.Error != "",
"text-success": data.Valid,
"min-h-[1rem]": true,
}) }
>
if data.Valid {
if data.Type == reddit.SubredditTypeUser {
Username target is valid
} else {
Subreddit is valid
}
} else {
{ data.Error }
}
</span>
</div>
}
templ subredditInputField(target string, data SubredditInputData) {
<input
type="text"
id="name"
name="name"
hx-post={ target }
hx-target="#subreddit-input"
hx-target-4xx="#subreddit-input"
hx-trigger="keyup changed delay:1s, on-demand"
hx-include="[name='type']"
hx-target-5x={ components.NotificationContainerID }
value={ data.Value }
placeholder="e.g. 'wallpaper' or 'EarthPorn'"
class={ utils.CX(map[string]bool{
"input": true,
"input-bordered": true,
"input-error": data.Error != "",
"text-error": data.Error != "",
"input-success": data.Valid,
"text-success": data.Valid,
}) }
required
data-error={ data.Error }
hx-on::load="this.setCustomValidity(this.getAttribute('data-error'))"
/>
}

View file

@ -0,0 +1,27 @@
package addview
type SubredditTypeData struct {
Value string
}
templ SubredditTypeInput(data SubredditTypeData) {
<div class="label">
<span class="label-text text-base">Subreddit Type</span>
</div>
<select
onchange="htmx.trigger('#name', 'on-demand')"
name="type"
value={ data.Value }
class="select select-bordered"
>
if data.Value == "1" {
<option value="0">Subreddit</option>
<option selected value="1">User</option>
} else {
<option selected value="0">Subreddit</option>
<option value="1">User</option>
}
</select>
<div class="min-h-4"></div>
<script>document.addEventListener('DOMContentLoaded', () => htmx.trigger('#name', 'on-demand')) </script>
}