diff --git a/http/go.mod b/http/go.mod index c1584a2..1940cfe 100644 --- a/http/go.mod +++ b/http/go.mod @@ -5,7 +5,7 @@ go 1.23.3 toolchain go1.24.5 require ( - github.com/Teajey/rsvp v0.11.0 + github.com/Teajey/rsvp v0.13.0 github.com/Teajey/sqlstore v0.0.6 github.com/gorilla/sessions v1.4.0 golang.org/x/crypto v0.22.0 diff --git a/http/go.sum b/http/go.sum index caa1377..c7a7bc5 100644 --- a/http/go.sum +++ b/http/go.sum @@ -1,5 +1,5 @@ -github.com/Teajey/rsvp v0.11.0 h1:ePx7Oj0nDYRCmFB6fQvSQbYUcWhgqTh9fh8PaHlOnwk= -github.com/Teajey/rsvp v0.11.0/go.mod h1:WCWos0l+K/9heUuvbIUXkKAHAtxoLpkJ43C/fszD4RY= +github.com/Teajey/rsvp v0.13.0 h1:EeuHMHtU2/eLuSbCIlzKqy5FI9f6Qq+yUJnrVPBJvKk= +github.com/Teajey/rsvp v0.13.0/go.mod h1:WCWos0l+K/9heUuvbIUXkKAHAtxoLpkJ43C/fszD4RY= github.com/Teajey/sqlstore v0.0.6 h1:kUEpA+3BKFHZl128MuMeYY6zVcmq1QmOlNyofcFEJOA= github.com/Teajey/sqlstore v0.0.6/go.mod h1:hjk0S593/2Q4QxkEXCgpThj9w5KWGTQi9JtgfziHXXk= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= diff --git a/http/response/handler.go b/http/response/handler.go index 1a8585b..8749c13 100644 --- a/http/response/handler.go +++ b/http/response/handler.go @@ -1,7 +1,6 @@ package response import ( - "fmt" "lishwist/http/templates" "log" "net/http" @@ -17,7 +16,7 @@ type ServeMux struct { func NewServeMux(store *sqlstore.Store) *ServeMux { mux := rsvp.NewServeMux() - mux.Config.HtmlTemplate = templates.Templates["login.gotmpl"] + mux.Config.HtmlTemplate = templates.Template return &ServeMux{ inner: mux, store: store, @@ -35,7 +34,7 @@ type Handler interface { type HandlerFunc func(*Session, http.Header, *http.Request) rsvp.Response func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) { - m.inner.Std.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { + m.inner.MiddleHandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) rsvp.Response { session := m.GetSession(r) response := handler(session, w.Header(), r) @@ -47,10 +46,7 @@ func (m *ServeMux) HandleFunc(pattern string, handler HandlerFunc) { } } - err := response.Write(w, r, m.inner.Config) - if err != nil { - panic(fmt.Sprintf("Failed to write rsvp.Response: %s", err)) - } + return response }) } diff --git a/http/response/session.go b/http/response/session.go index dd8010c..d5e70fc 100644 --- a/http/response/session.go +++ b/http/response/session.go @@ -11,10 +11,10 @@ type Session struct { func (s *Session) FlashGet() any { list := s.inner.Flashes() - s.written = true if len(list) < 1 { return nil } else { + s.written = true return list[0] } } diff --git a/http/routing/foreign_wishlist.go b/http/routing/foreign_wishlist.go index 3cb76b6..366b508 100644 --- a/http/routing/foreign_wishlist.go +++ b/http/routing/foreign_wishlist.go @@ -19,7 +19,7 @@ type foreignWishlistProps struct { func ForeignWishlist(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response { userReference := r.PathValue("userReference") if app.User.Reference == userReference { - return rsvp.SeeOther("/") + return rsvp.Found("/", "You're not allowed to view your own wishlist!") } otherUser, err := lishwist.GetUserByReference(userReference) if err != nil { diff --git a/http/routing/login.go b/http/routing/login.go index 5a34fd4..05fcbb4 100644 --- a/http/routing/login.go +++ b/http/routing/login.go @@ -44,12 +44,14 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R props := api.NewLoginProps(username, password) + resp := rsvp.SeeOther(r.URL.Path, props) + valid := props.Validate() props.Password.Value = "" if !valid { session.FlashSet(&props) log.Printf("Invalid props: %#v\n", props) - return rsvp.SeeOther("/") + return resp } app, err := lishwist.Login(username, password) @@ -59,12 +61,12 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R props.GeneralError = "Username or password invalid" session.FlashSet(&props) log.Printf("Invalid credentials: %s: %#v\n", err, props) - return rsvp.SeeOther("/") + return resp default: props.GeneralError = "Something went wrong." session.FlashSet(&props) log.Printf("Login error: %s\n", err) - return rsvp.SeeOther("/") + return resp } } @@ -72,5 +74,5 @@ func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.R session.SetValue("authorized", true) session.SetValue("username", app.User.Name) - return rsvp.SeeOther(r.URL.Path) + return rsvp.SeeOther(r.URL.Path, "Login successful!") } diff --git a/http/routing/logout.go b/http/routing/logout.go index 0500047..f32ffd6 100644 --- a/http/routing/logout.go +++ b/http/routing/logout.go @@ -11,5 +11,5 @@ func LogoutPost(session *response.Session, h http.Header, r *http.Request) rsvp. session.Options().MaxAge = 0 session.ClearValues() - return rsvp.SeeOther("/") + return rsvp.SeeOther("/", "Logout successful") } diff --git a/http/routing/register.go b/http/routing/register.go index add5b73..dc1b6c3 100644 --- a/http/routing/register.go +++ b/http/routing/register.go @@ -45,8 +45,8 @@ func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Resp props.ConfirmPassword.Value = "" if !valid { s.FlashSet(&props) - log.Printf("Invalid props: %#v\n", props) - return rsvp.SeeOther("/") + log.Printf("Invalid register props: %#v\n", props) + return rsvp.SeeOther(r.URL.Path, props) } _, err = lishwist.Register(username, newPassword) @@ -58,9 +58,9 @@ func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Resp } s.FlashSet(&props) log.Printf("Registration failed: %s\n", err) - return rsvp.SeeOther("/register") + return rsvp.SeeOther(r.URL.Path, props) } s.FlashSet(true) - return rsvp.SeeOther("/") + return rsvp.SeeOther("/", "Registration successful!") } diff --git a/http/routing/todo.go b/http/routing/todo.go index 5daab4f..91a7823 100644 --- a/http/routing/todo.go +++ b/http/routing/todo.go @@ -34,5 +34,5 @@ func TodoUpdate(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Resp default: return response.Error(http.StatusBadRequest, "Invalid intent") } - return rsvp.SeeOther("/") + return rsvp.SeeOther("/", "Update successful") } diff --git a/http/routing/wishlist.go b/http/routing/wishlist.go index a1015c9..4b7c783 100644 --- a/http/routing/wishlist.go +++ b/http/routing/wishlist.go @@ -22,7 +22,7 @@ func WishlistAdd(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Res log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to add gift.") } - return rsvp.SeeOther("/") + return rsvp.SeeOther("/", "Wish added!") } func WishlistDelete(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response { @@ -37,7 +37,7 @@ func WishlistDelete(app *lishwist.Session, h http.Header, r *http.Request) rsvp. log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to remove gifts.") } - return rsvp.SeeOther("/") + return rsvp.SeeOther("/", "Wish deleted") } func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response { @@ -47,6 +47,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) } userReference := r.PathValue("userReference") + resp := rsvp.SeeOther("/list/"+userReference, "Update successful") intent := r.Form.Get("intent") switch intent { case "claim": @@ -57,6 +58,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to update claim...") } + resp.Body = "Successfully claimed wishes" case "complete": claims := r.Form["claimed"] err := app.CompleteWishes(claims) @@ -64,6 +66,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to complete gifts...") } + resp.Body = "Successfully completed wishes" case "add": wishName := r.Form.Get("gift_name") if wishName == "" { @@ -74,6 +77,7 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to add gift idea to other user...") } + resp.Body = "Successfully added wishes" case "delete": claims := r.Form["unclaimed"] unclaims := r.Form["claimed"] @@ -83,8 +87,9 @@ func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *http.Request) log.Printf("%s\n", err) return response.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...") } + resp.Body = "Successfully removed wishes" default: return response.Error(http.StatusBadRequest, "Invalid intent %q", intent) } - return rsvp.SeeOther("/list/" + userReference) + return resp } diff --git a/http/templates/base.gotmpl b/http/templates/base.gotmpl index 5407dc1..fcfa5f1 100644 --- a/http/templates/base.gotmpl +++ b/http/templates/base.gotmpl @@ -12,10 +12,7 @@ {{end}} {{end}} - - - - +{{define "head"}} Lishwist @@ -42,6 +39,11 @@ submitter.disabled = !accepted; } +{{end}} + +{{define "boilerplate"}} + + {{template "head" .}} @@ -61,5 +63,8 @@ {{template "body" .}} +{{end}} - +{{define "login_prompt"}} +Login or register +{{end}} diff --git a/http/templates/error_page.gotmpl b/http/templates/error_page.gotmpl index 8903d5f..d51f2a6 100644 --- a/http/templates/error_page.gotmpl +++ b/http/templates/error_page.gotmpl @@ -1,17 +1,34 @@ -{{define "navbar"}} - -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
- -
-{{end}} + +
+ +
+ +
+
+ + diff --git a/http/templates/foreign_wishlist.gotmpl b/http/templates/foreign_wishlist.gotmpl index 743a771..dd3f518 100644 --- a/http/templates/foreign_wishlist.gotmpl +++ b/http/templates/foreign_wishlist.gotmpl @@ -1,98 +1,115 @@ -{{define "navbar"}} - -
- -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
-
-
-
-

{{.Username}}'s list

- {{with .Gifts}} -
-
    - {{range .}} - {{$isMine := eq .ClaimantId $.CurrentUserId}} - {{$createdByMe := eq .CreatorId $.CurrentUserId}} - {{$outsideIdea := ne .RecipientId .CreatorId}} -
  • - - - - - - {{if or .ClaimantId $outsideIdea}} -
    - {{if .ClaimantId}} - {{if .Sent}}Completed{{else}}Claimed{{end}} - by - {{if $isMine}}you{{else}}{{.ClaimantName}}{{end}} - {{end}} - - {{if $outsideIdea}} - Added by {{if - $createdByMe}}you{{else}}{{.CreatorName}}{{end}} - {{end}} -
    - {{end}} -
  • - {{end}} -
- - - -
- {{else}} -

They don't have any gift ideas. Ask them to think of something, or add an idea yourself! 👇 (everyone - except them will be able to see it and claim it)

- {{end}} -
-
- - + +
+ -
-
-
-{{end}} +
+
+
+
+

{{.Username}}'s list

+ {{with .Gifts}} +
+
    + {{range .}} + {{$isMine := eq .ClaimantId $.CurrentUserId}} + {{$createdByMe := eq .CreatorId $.CurrentUserId}} + {{$outsideIdea := ne .RecipientId .CreatorId}} +
  • + + + + + + {{if or .ClaimantId $outsideIdea}} +
    + {{if .ClaimantId}} + {{if .Sent}}Completed{{else}}Claimed{{end}} + by + {{if $isMine}}you{{else}}{{.ClaimantName}}{{end}} + {{end}} + + {{if $outsideIdea}} + Added by {{if + $createdByMe}}you{{else}}{{.CreatorName}}{{end}} + {{end}} +
    + {{end}} +
  • + {{end}} +
+ + + +
+ {{else}} +

They don't have any gift ideas. Ask them to think of something, or add an idea yourself! 👇 (everyone + except them will be able to see it and claim it)

+ {{end}} +
+
+ + +
+
This will be invisible to {{.Username}}, but everyone else will be + able to see it and + possibly claim it.
+
+
+
+
+
+ + + diff --git a/http/templates/group_page.gotmpl b/http/templates/group_page.gotmpl index 035f4b8..4b6d26e 100644 --- a/http/templates/group_page.gotmpl +++ b/http/templates/group_page.gotmpl @@ -1,50 +1,66 @@ -{{define "navbar"}} - -
- -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
-
-
-
-

{{.Group.Name}} group members

- {{with .Group.Members}} - - {{else}} -

There's nobody else in this group.

- {{end}} + +
+ -
-
-
-{{end}} - +
+
+
+
+

{{.Group.Name}} group members

+ {{with .Group.Members}} + + {{else}} +

There's nobody else in this group.

+ {{end}} +
+
+
+
+ + + diff --git a/http/templates/home.gotmpl b/http/templates/home.gotmpl index cc6e508..f7a85e1 100644 --- a/http/templates/home.gotmpl +++ b/http/templates/home.gotmpl @@ -1,113 +1,130 @@ -{{define "navbar"}} -
- -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
-
-
-
-

Your wishlist

- {{with .Gifts}} -
-
    - {{range .}} -
  • - - -
  • - {{end}} -
- -
- {{else}} -

Your list is empty. Think of some things to add!

- {{end}} -
-
- - + +
+ -
- -
-
-

Your todo list

- {{with .Todo}} -
-
    - {{range .}} -
  • - -
+ + + {{else}} +

Your list is empty. Think of some things to add!

+ {{end}} +
+
+ + +
+
+
+
-
-
-

Your groups

- {{with .Groups}} - - {{else}} -

You don't belong to any groups

- {{end}} +
+
+

Your todo list

+ {{with .Todo}} +
+
    + {{range .}} +
  • + + + + for {{.RecipientName}} + +
  • + {{end}} +
+ + +
+ {{else}} +

When you claim gifts for others, they will appear here.

+ {{end}} +
+
+ +
+
+

Your groups

+ {{with .Groups}} + + {{else}} +

You don't belong to any groups

+ {{end}} +
+
+
- -
- -{{end}} + + + diff --git a/http/templates/login.gotmpl b/http/templates/login.gotmpl index 158a608..8f53bb2 100644 --- a/http/templates/login.gotmpl +++ b/http/templates/login.gotmpl @@ -1,33 +1,50 @@ -{{define "navbar"}} -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
- {{if .SuccessfulRegistration}} - - {{end}} - {{with .GeneralError}} - - {{end}} -
-
- - -
- Register + +
+ +
+ {{if .SuccessfulRegistration}} + + {{end}} + {{with .GeneralError}} + + {{end}} + +
+ + +
+ Register +
+ +
+
-
- -
-{{end}} + + diff --git a/http/templates/public_foreign_wishlist.gotmpl b/http/templates/public_foreign_wishlist.gotmpl index 3cd4206..07f83da 100644 --- a/http/templates/public_foreign_wishlist.gotmpl +++ b/http/templates/public_foreign_wishlist.gotmpl @@ -1,37 +1,50 @@ -{{define "navbar"}} - -{{end}} + + + + {{template "head" .}} + -{{define "login_prompt"}} -Login or register -{{end}} - -{{define "body"}} -
-
-
-
-

{{.Username}}'s list

- {{if eq .GiftCount 0}} -

{{.Username}} hasn't written any gift ideas!

-

{{template "login_prompt"}} to add some! :^)

- {{else}} - {{if eq .GiftCount 1}} -

{{.Username}} has only written one gift idea.

-

{{template "login_prompt"}} to claim it, or add more! :^)

- {{else}} -

{{.Username}} has written {{.GiftCount}} gift ideas.

-

{{template "login_prompt"}} to claim an idea, or add more! :^)

- {{end}} - {{end}} + +
+ -
-
-
-{{end}} +
+
+
+
+

{{.Username}}'s list

+ {{if eq .GiftCount 0}} +

{{.Username}} hasn't written any gift ideas!

+

{{template "login_prompt"}} to add some! :^)

+ {{else}} + {{if eq .GiftCount 1}} +

{{.Username}} has only written one gift idea.

+

{{template "login_prompt"}} to claim it, or add more! :^)

+ {{else}} +

{{.Username}} has written {{.GiftCount}} gift ideas.

+

{{template "login_prompt"}} to claim an idea, or add more! :^)

+ {{end}} + {{end}} +
+
+
+
+
+ + diff --git a/http/templates/public_group_page.gotmpl b/http/templates/public_group_page.gotmpl index 0b05d6e..bb04ca0 100644 --- a/http/templates/public_group_page.gotmpl +++ b/http/templates/public_group_page.gotmpl @@ -1,37 +1,50 @@ -{{define "navbar"}} - -{{end}} + + + + {{template "head" .}} + -{{define "login_prompt"}} -Login or register -{{end}} - -{{define "body"}} -
-
-
-
-

{{.Group.Name}} group members

-

{{template "login_prompt"}} to see your groups

- {{with .Group.Members}} -
    - {{range .}} -
  • - {{.Name}} -
  • - {{end}} -
- {{else}} -

There's nobody else in this group.

- {{end}} + +
+ -
-
-
-{{end}} +
+
+
+
+

{{.Group.Name}} group members

+

{{template "login_prompt"}} to see your groups

+ {{with .Group.Members}} +
    + {{range .}} +
  • + {{.Name}} +
  • + {{end}} +
+ {{else}} +

There's nobody else in this group.

+ {{end}} +
+
+
+
+
+ + diff --git a/http/templates/register.gotmpl b/http/templates/register.gotmpl index 6a147f4..d92d65e 100644 --- a/http/templates/register.gotmpl +++ b/http/templates/register.gotmpl @@ -1,40 +1,57 @@ -{{define "navbar"}} - -{{end}} + + + + {{template "head" .}} + -{{define "body"}} -
- - {{with .GeneralError}} - - {{end}} -
-
- - - - + +
+ +
+ + {{with .GeneralError}} + + {{end}} + +
+ + + + +
+ +
- -
-{{end}} + + diff --git a/http/templates/templates.go b/http/templates/templates.go index fe6bd5b..135a187 100644 --- a/http/templates/templates.go +++ b/http/templates/templates.go @@ -29,29 +29,13 @@ func (p *InputProps) Validate() bool { return true } -var Templates map[string]*template.Template +var Template *template.Template func init() { - Templates = load() + Template = load() } -func load() map[string]*template.Template { - homeTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/home.gotmpl")) - loginTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/login.gotmpl")) - registerTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/register.gotmpl")) - foreignTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/foreign_wishlist.gotmpl")) - publicWishlistTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/public_foreign_wishlist.gotmpl")) - errorTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/error_page.gotmpl")) - groupTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/group_page.gotmpl")) - publicGroupTmpl := template.Must(template.ParseFiles("templates/base.gotmpl", "templates/public_group_page.gotmpl")) - return map[string]*template.Template{ - "home.gotmpl": homeTmpl, - "login.gotmpl": loginTmpl, - "register.gotmpl": registerTmpl, - "foreign_wishlist.gotmpl": foreignTmpl, - "public_foreign_wishlist.gotmpl": publicWishlistTmpl, - "error_page.gotmpl": errorTmpl, - "group_page.gotmpl": groupTmpl, - "public_group_page.gotmpl": publicGroupTmpl, - } +func load() *template.Template { + t := template.Must(template.ParseGlob("templates/*.gotmpl")) + return t }