Support editing garbage

This commit is contained in:
Adriano Caloiaro 2023-07-29 13:26:45 -06:00
parent 8fb9646d2c
commit add714d21f
No known key found for this signature in database
GPG key ID: C2BC56DE73CE3F75
7 changed files with 185 additions and 14 deletions

View file

@ -105,7 +105,7 @@ enableInlineShortcodes = true
[[languages.en.menu.main]] [[languages.en.menu.main]]
identifier = "submit" identifier = "submit"
name = "Submit" name = "Submit"
url = "/posts/new" url = "/garbage/new"
[module] [module]
# In case you would like to make changes to the theme and keep it locally in you repository, # In case you would like to make changes to the theme and keep it locally in you repository,

View file

@ -0,0 +1,46 @@
<h2>Edit Post</h2>
<form hx-put="{{ .ApiBaseUrl }}/garbage/{{ .Garbage.ID }}">
<label for="title">A brief summary of the garbage</label>
<input
id="title"
autofocus
rquired
type="text"
name="title"
style="width: 100%"
placeholder="Provide an explanation, title, or some context"
value="{{ .Garbage.Title }}"/>
<label for="garbage">Garbage Speak</label>
<textarea id="garbage"
required
minlength="10"
rows="3"
autocorrect="off"
wrap="soft"
name="garbage"
style="width: 100%"
placeholder="This may be any garbage speak seen in the wild. Refer to the FAQ for what constitues garbage
speak.">{{.Garbage.Content}}</textarea>
<label for="url">URL where seen (optional)</label>
<input
id="url"
optional
type="text"
name="url"
style="width: 100%"
placeholder="A public URL where this garbage was seen"
value="{{ .Garbage.Url }}"/>
<label for="tags">Tags (optional)</label>
<select id="tags" name="tags" multiple optional>
{{ range $tag := .AvailableTags }}
<option {{ if index $.SelectedTags $tag}} selected{{end}}>{{ $tag }}</option>
{{ end }}
</select>
<br>
<br>
<button>Update</button>
</form>

View file

@ -1,14 +1,14 @@
<div class="posts"> <div class="posts">
{{ $apiBaseURL := .ApiBaseUrl }}
{{range .Posts}} {{range .Posts}}
<article class="post on-list"> <article class="post on-list">
<h1 class="post-title">{{ .Title }}</h1> <h1 class="post-title"><a href={{ .Url }}>{{ .Title }}</a></h1>
<div class="post-meta"> <div class="post-meta">
{{- with .Username -}} {{- with .Username -}}
<span>Submitter:&nbsp;<span class="post-author">{{ . }}</span></span> <span>Submitter:&nbsp;{{ . }}</span>
{{- end -}} {{- end -}}
{{ if .Metadata.tags }} {{ if .Metadata.tags }}
<span class=""> <span>
{{ range .Metadata.tags }} {{ range .Metadata.tags }}
{{- . -}}, {{- . -}},
{{ end }} {{ end }}
@ -20,6 +20,13 @@
{{- .Format "2006-01-02" -}} {{- .Format "2006-01-02" -}}
</time> </time>
{{- end -}} {{- end -}}
{{ if $.LoggedIn }}
<a href="#"
hx-get="{{ $apiBaseURL }}/garbage/{{ .ID }}/edit"
hx-target="#content"
hx-swap="outerHTML">Edit</a>
{{ end }}
</div> </div>

130
server.go
View file

@ -173,6 +173,8 @@ func main() {
}) })
r.Route("/garbage", func(r chi.Router) { r.Route("/garbage", func(r chi.Router) {
r.Post("/new", createGarbageHandler) r.Post("/new", createGarbageHandler)
r.Get("/{garbage_id}/edit", editGarbageHandler)
r.Put("/{garbage_id}", editGarbageUpdateHandler)
}) })
fmt.Printf("Starting API server on port 1314\n") fmt.Printf("Starting API server on port 1314\n")
@ -181,6 +183,113 @@ func main() {
} }
} }
func editGarbageUpdateHandler(w http.ResponseWriter, r *http.Request) {
garbageID := chi.URLParam(r, "garbage_id")
userID := sessions.GetString(r.Context(), "userID")
if userID == "" {
ise(errors.New("not logged in"), w)
return
}
if err := r.ParseForm(); err != nil {
ise(err, w)
return
}
url := r.PostForm.Get("url")
title := r.PostForm.Get("title")
garbage := r.PostForm.Get("garbage")
// TODO this is an arbitrary length that will likely need to change
if len(garbage) == 10 {
w.WriteHeader(400)
return
}
metadata := map[string]any{}
tags := r.Form["tags"]
if len(tags) > 0 {
metadata["tags"] = tags
}
ctx := context.Background()
_, err := db.Exec(ctx,
"UPDATE garbages SET (title, content, url, metadata) = ($1, $2, $3, $4) WHERE id = $5",
title,
garbage,
url,
metadata,
garbageID)
if err != nil {
log.Println(err)
return
}
log.Println("HERE")
//var buff = bytes.NewBufferString("")
//tmpl := template.Must(template.ParseFiles("partials/users/created.html"))
//err = tmpl.Execute(buff, map[string]any{"Email": email})
//if err != nil {
//ise(err, w)
//return
//}
w.Header().Add("hx-location", baseURL())
}
func editGarbageHandler(w http.ResponseWriter, r *http.Request) {
garbageID := chi.URLParam(r, "garbage_id")
userID := sessions.GetString(r.Context(), "userID")
if userID == "" {
return // TODO render an error
}
garbage := Garbage{}
ctx := context.Background()
err := pgxscan.Get(
ctx,
db,
&garbage,
`SELECT id, title, content, metadata, url FROM garbages WHERE id = $1`, garbageID)
if err != nil {
ise(err, w)
return
}
availableTags := []string{
"Nouned verb",
"Verbed noun",
"Nouned adjective",
"Novel garbage",
}
selectedTags := map[string]bool{}
if tags, ok := garbage.Metadata["tags"].([]interface{}); ok {
for _, tag := range tags {
selectedTags[tag.(string)] = true
}
}
tmplVars := map[string]any{
"ApiBaseUrl": apiURL(),
"Garbage": garbage,
"SelectedTags": selectedTags,
"AvailableTags": availableTags,
}
tmpl := template.Must(template.ParseFiles("partials/garbage/edit.html"))
var buff = bytes.NewBufferString("")
err = tmpl.Execute(buff, tmplVars)
if err != nil {
ise(err, w)
return
}
w.Header().Add("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write(buff.Bytes())
}
// newUserValidationHandler checks whether a username is available during account creation // newUserValidationHandler checks whether a username is available during account creation
func newUserValidationHandler(w http.ResponseWriter, r *http.Request) { func newUserValidationHandler(w http.ResponseWriter, r *http.Request) {
tmplVars := map[string]any{"ApiBaseUrl": apiURL()} tmplVars := map[string]any{"ApiBaseUrl": apiURL()}
@ -292,7 +401,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
// logoutHandler handles users logout requests // logoutHandler handles users logout requests
func logoutHandler(w http.ResponseWriter, r *http.Request) { func logoutHandler(w http.ResponseWriter, r *http.Request) {
sessions.Clear(r.Context()) sessions.Destroy(r.Context())
http.Redirect(w, r, baseURL(), http.StatusFound) http.Redirect(w, r, baseURL(), http.StatusFound)
} }
@ -301,7 +410,6 @@ func navUserItems(w http.ResponseWriter, r *http.Request) {
userID := sessions.GetString(r.Context(), "userID") userID := sessions.GetString(r.Context(), "userID")
var tmpl *template.Template var tmpl *template.Template
log.Println("user id:", userID)
if userID == "" { if userID == "" {
tmpl = template.Must(template.ParseFiles("partials/nav/non_user_nav_items.html")) tmpl = template.Must(template.ParseFiles("partials/nav/non_user_nav_items.html"))
} else { } else {
@ -371,7 +479,6 @@ func emailVerification(w http.ResponseWriter, r *http.Request) {
} }
func createGarbageHandler(w http.ResponseWriter, r *http.Request) { func createGarbageHandler(w http.ResponseWriter, r *http.Request) {
userID := sessions.GetString(r.Context(), "userID") userID := sessions.GetString(r.Context(), "userID")
if userID == "" { if userID == "" {
ise(errors.New("not logged in"), w) ise(errors.New("not logged in"), w)
@ -392,9 +499,11 @@ func createGarbageHandler(w http.ResponseWriter, r *http.Request) {
} }
url := r.PostForm.Get("url") url := r.PostForm.Get("url")
metadata := map[string]any{}
tags := r.Form["tags"] tags := r.Form["tags"]
metadata := map[string]any{ if len(tags) > 0 {
"tags": tags, metadata["tags"] = tags
} }
ctx := context.Background() ctx := context.Background()
@ -526,7 +635,11 @@ func garbageBin(w http.ResponseWriter, r *http.Request) {
var buff = bytes.NewBufferString("") var buff = bytes.NewBufferString("")
tmpl := template.Must(template.ParseFiles("partials/posts.html")) tmpl := template.Must(template.ParseFiles("partials/posts.html"))
err = tmpl.Execute(buff, map[string]any{"Posts": posts}) err = tmpl.Execute(buff, map[string]any{
"Posts": posts,
"ApiBaseUrl": apiURL(),
"LoggedIn": isLoggedIn(r),
})
if err != nil { if err != nil {
ise(err, w) ise(err, w)
return return
@ -683,3 +796,8 @@ func ise(err error, w http.ResponseWriter) {
fmt.Fprintf(w, "error: %v", err) fmt.Fprintf(w, "error: %v", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
} }
func isLoggedIn(r *http.Request) bool {
var _, err = r.Cookie("session_id")
return err == nil
}

View file

@ -29,11 +29,11 @@
&-meta { &-meta {
font-size: 1rem; font-size: 1rem;
margin-bottom: 10px; margin-bottom: 10px;
color: transparentize($accent, .3); color: transparentize($accent, .6);
& > *:not(:first-child) { & > *:not(:first-child) {
&::before { &::before {
content: "::"; content: "|";
display: inline-block; display: inline-block;
margin: 0 8px; margin: 0 8px;
} }

View file

@ -13,7 +13,7 @@
{{ partial "header.html" . }} {{ partial "header.html" . }}
<div class="content"> <div id="content" class="content">
{{ block "main" . }} {{ block "main" . }}
{{ end }} {{ end }}
</div> </div>