subreddit-add: enhanced subreddit check and add form
This commit is contained in:
parent
15f9634187
commit
4e7f91d098
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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'))"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
73
views/subredditsview/addview/subreddit_name_input.templ
Normal file
73
views/subredditsview/addview/subreddit_name_input.templ
Normal 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'))"
|
||||||
|
/>
|
||||||
|
}
|
27
views/subredditsview/addview/subreddit_type_input.templ
Normal file
27
views/subredditsview/addview/subreddit_type_input.templ
Normal 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>
|
||||||
|
}
|
Loading…
Reference in a new issue