diff --git a/input.css b/input.css index a461c50..7c83bae 100644 --- a/input.css +++ b/input.css @@ -1 +1,7 @@ -@import "tailwindcss"; \ No newline at end of file +@import "tailwindcss"; + +@utility container { + margin-inline: auto; + padding: 2rem 4rem; + max-width: 42rem; +} diff --git a/internal/server/htmx/htmx.go b/internal/server/htmx/htmx.go new file mode 100644 index 0000000..040cc53 --- /dev/null +++ b/internal/server/htmx/htmx.go @@ -0,0 +1,23 @@ +package htmx + +import ( + "fmt" + "net/http" +) + +func HxNotice(w http.ResponseWriter, id, msg string) { + html := fmt.Sprintf(`%s`, id, msg) + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + w.Write([]byte(html)) +} + +func HxError(w http.ResponseWriter, status int, msg string) { + w.WriteHeader(status) + w.Write([]byte(msg)) +} + +func HxRedirect(w http.ResponseWriter, status int, location string) { + w.Header().Set("HX-Redirect", location) + w.WriteHeader(status) +} diff --git a/internal/server/index.go b/internal/server/index.go index 720b1f0..c52fd39 100644 --- a/internal/server/index.go +++ b/internal/server/index.go @@ -3,9 +3,9 @@ package server import ( "net/http" - "shelf.app/internal/server/views" + "shelf.app/internal/views/index" ) -func (s *Server) HandleIndexPage(w http.ResponseWriter, r *http.Request) { - views.IndexPage(views.IndexPageParams{}).Render(r.Context(), w) +func (s *Server) Index(w http.ResponseWriter, r *http.Request) { + index.IndexPage(index.IndexPageParams{}).Render(r.Context(), w) } diff --git a/internal/server/login.go b/internal/server/login.go new file mode 100644 index 0000000..f4bb968 --- /dev/null +++ b/internal/server/login.go @@ -0,0 +1,50 @@ +package server + +import ( + "fmt" + "net/http" + "strings" + + "shelf.app/internal/server/htmx" + "shelf.app/internal/views/login" +) + +func (s *Server) Login(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + returnURL := r.URL.Query().Get("return_url") + errorCode := r.URL.Query().Get("error") + login.LoginPage(login.LoginPageParams{ + ReturnUrl: returnURL, + ErrorCode: errorCode, + }).Render(r.Context(), w) + case http.MethodPost: + handle := r.FormValue("handle") + returnURL := r.FormValue("return_url") + + // When users copy their handle from bsky.app, it tends to have these + // characters around it: + // + // @nelind.dk: + // \u202a ensures that the handle is always rendered left to right and + // \u202c reverts that so the rest of the page renders however it should + handle = strings.TrimPrefix(handle, "\u202a") + handle = strings.TrimSuffix(handle, "\u202c") + + // `@` is harmless + handle = strings.TrimPrefix(handle, "@") + + // Basic handle validation + if !strings.Contains(handle, ".") { + w.Header().Set("Content-Type", "text/html") + login.LoginFormContent(login.LoginFormParams{ + ReturnUrl: returnURL, + Handle: handle, + ErrorMessage: fmt.Sprintf("'%s' is an invalid handle. Did you mean %s.bsky.social?", handle, handle), + }).Render(r.Context(), w) + return + } + + htmx.HxRedirect(w, http.StatusOK, "/") + } +} diff --git a/internal/server/router.go b/internal/server/router.go index 7627415..2d37165 100644 --- a/internal/server/router.go +++ b/internal/server/router.go @@ -9,8 +9,12 @@ import ( func (s *Server) Router() http.Handler { router := chi.NewRouter() - router.Get("/", s.HandleIndexPage) router.Handle("/static/*", s.HandleStatic()) + router.Get("/", s.Index) + + router.Get("/login", s.Login) + router.Post("/login", s.Login) + return router }