forked from tangled.org/core
this repo has no description

appview/pages: rework signup pages

- new /signup page to enter email ID
- login page has a link to go signup
- signup page has a link to go back to login

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 564cfdd2 fd66928f

verified
Changed files
+134 -136
appview
+5 -3
appview/pages/pages.go
···
return p.executePlain("user/login", w, params)
}
-
type SignupParams struct{}
+
func (p *Pages) Signup(w io.Writer) error {
+
return p.executePlain("user/signup", w, nil)
+
}
-
func (p *Pages) CompleteSignup(w io.Writer, params SignupParams) error {
-
return p.executePlain("user/completeSignup", w, params)
+
func (p *Pages) CompleteSignup(w io.Writer) error {
+
return p.executePlain("user/completeSignup", w, nil)
}
type TermsOfServiceParams struct {
+4
appview/pages/templates/layouts/topbar.html
···
{{ block "dropDown" . }} {{ end }}
{{ else }}
<a href="/login">login</a>
+
<span class="text-gray-500 dark:text-gray-400">or</span>
+
<a href="/signup" class="btn-create py-0 hover:no-underline hover:text-white flex items-center gap-2">
+
join now {{ i "arrow-right" "size-4" }}
+
</a>
{{ end }}
</div>
</div>
+5 -5
appview/pages/templates/user/completeSignup.html
···
tightly-knit social coding.
</h2>
<form
-
class="mt-4 max-w-sm mx-auto"
+
class="mt-4 max-w-sm mx-auto flex flex-col gap-4"
hx-post="/signup/complete"
hx-swap="none"
hx-disabled-elt="#complete-signup-button"
···
</span>
</div>
-
<div class="flex flex-col mt-4">
-
<label for="username">desired username</label>
+
<div class="flex flex-col">
+
<label for="username">username</label>
<input
type="text"
id="username"
···
</span>
</div>
-
<div class="flex flex-col mt-4">
+
<div class="flex flex-col">
<label for="password">password</label>
<input
type="password"
···
</div>
<button
-
class="btn-create w-full my-2 mt-6"
+
class="btn-create w-full my-2 mt-6 text-base"
type="submit"
id="complete-signup-button"
tabindex="4"
+11 -79
appview/pages/templates/user/login.html
···
<html lang="en" class="dark:bg-gray-900">
<head>
<meta charset="UTF-8" />
-
<meta
-
name="viewport"
-
content="width=device-width, initial-scale=1.0"
-
/>
-
<meta
-
property="og:title"
-
content="login · tangled"
-
/>
-
<meta
-
property="og:url"
-
content="https://tangled.sh/login"
-
/>
-
<meta
-
property="og:description"
-
content="login to or sign up for tangled"
-
/>
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
<meta property="og:title" content="login · tangled" />
+
<meta property="og:url" content="https://tangled.sh/login" />
+
<meta property="og:description" content="login to for tangled" />
<script src="/static/htmx.min.js"></script>
-
<link
-
rel="stylesheet"
-
href="/static/tw.css?{{ cssContentHash }}"
-
type="text/css"
-
/>
-
<title>login or sign up &middot; tangled</title>
+
<link rel="stylesheet" href="/static/tw.css?{{ cssContentHash }}" type="text/css" />
+
<title>login &middot; tangled</title>
</head>
<body class="flex items-center justify-center min-h-screen">
<main class="max-w-md px-6 -mt-4">
-
<h1
-
class="text-center text-2xl font-semibold italic dark:text-white"
-
>
+
<h1 class="text-center text-2xl font-semibold italic dark:text-white" >
tangled
</h1>
<h2 class="text-center text-xl italic dark:text-white">
···
name="handle"
tabindex="1"
required
-
placeholder="foo.tngl.sh"
+
placeholder="akshay.tngl.sh"
/>
<span class="text-sm text-gray-500 mt-1">
Use your <a href="https://atproto.com">ATProto</a>
···
</div>
<button
-
class="btn w-full my-2 mt-6"
+
class="btn w-full my-2 mt-6 text-base "
type="submit"
id="login-button"
tabindex="3"
···
<span>login</span>
</button>
</form>
-
<hr class="my-4">
-
<p class="text-sm text-gray-500 mt-4">
-
Alternatively, you may create an account on Tangled below. You will
-
get a <code>user.tngl.sh</code> handle.
+
<p class="text-sm text-gray-500">
+
Don't have an account? <a href="/signup" class="underline">Create an account</a> on Tangled now!
</p>
-
<details class="group">
-
-
<summary
-
class="btn cursor-pointer w-full mt-4 flex items-center justify-center gap-2"
-
>
-
create an account
-
-
<div class="group-open:hidden flex">{{ i "arrow-right" "w-4 h-4" }}</div>
-
<div class="hidden group-open:flex">{{ i "arrow-down" "w-4 h-4" }}</div>
-
</summary>
-
<form
-
class="mt-4 max-w-sm mx-auto"
-
hx-post="/signup"
-
hx-swap="none"
-
hx-disabled-elt="#signup-button"
-
>
-
<div class="flex flex-col mt-2">
-
<label for="email">email</label>
-
<input
-
type="email"
-
id="email"
-
name="email"
-
tabindex="4"
-
required
-
placeholder="jason@bourne.co"
-
/>
-
</div>
-
<span class="text-sm text-gray-500 mt-1">
-
You will receive an email with a code. Enter that, along with your
-
desired username and password in the next page to complete your registration.
-
</span>
-
<button
-
class="btn w-full my-2 mt-6"
-
type="submit"
-
id="signup-button"
-
tabindex="7"
-
>
-
<span>sign up</span>
-
</button>
-
</form>
-
</details>
-
<p class="text-sm text-gray-500 mt-6">
-
Join our <a href="https://chat.tangled.sh">Discord</a> or
-
IRC channel:
-
<a href="https://web.libera.chat/#tangled"
-
><code>#tangled</code> on Libera Chat</a
-
>.
-
</p>
<p id="login-msg" class="error w-full"></p>
</main>
</body>
+53
appview/pages/templates/user/signup.html
···
+
{{ define "user/signup" }}
+
<!doctype html>
+
<html lang="en" class="dark:bg-gray-900">
+
<head>
+
<meta charset="UTF-8" />
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
<meta property="og:title" content="signup · tangled" />
+
<meta property="og:url" content="https://tangled.sh/signup" />
+
<meta property="og:description" content="sign up for tangled" />
+
<script src="/static/htmx.min.js"></script>
+
<link rel="stylesheet" href="/static/tw.css?{{ cssContentHash }}" type="text/css" />
+
<title>sign up &middot; tangled</title>
+
</head>
+
<body class="flex items-center justify-center min-h-screen">
+
<main class="max-w-md px-6 -mt-4">
+
<h1 class="text-center text-2xl font-semibold italic dark:text-white" >tangled</h1>
+
<h2 class="text-center text-xl italic dark:text-white">tightly-knit social coding.</h2>
+
<form
+
class="mt-4 max-w-sm mx-auto"
+
hx-post="/signup"
+
hx-swap="none"
+
hx-disabled-elt="#signup-button"
+
>
+
<div class="flex flex-col mt-2">
+
<label for="email">email</label>
+
<input
+
type="email"
+
id="email"
+
name="email"
+
tabindex="4"
+
required
+
placeholder="jason@bourne.co"
+
/>
+
</div>
+
<span class="text-sm text-gray-500 mt-1">
+
You will receive an email with an invite code. Enter your
+
invite code, desired username, and password in the next
+
page to complete your registration.
+
</span>
+
<button class="btn text-base w-full my-2 mt-6" type="submit" id="signup-button" tabindex="7" >
+
<span>join now</span>
+
</button>
+
</form>
+
<p class="text-sm text-gray-500">
+
Already have an account? <a href="/login" class="underline">Login to Tangled</a>.
+
</p>
+
+
<p id="signup-msg" class="error w-full"></p>
+
</main>
+
</body>
+
</html>
+
{{ end }}
+
+56 -49
appview/signup/signup.go
···
func (s *Signup) Router() http.Handler {
r := chi.NewRouter()
+
r.Get("/", s.signup)
r.Post("/", s.signup)
r.Get("/complete", s.complete)
r.Post("/complete", s.complete)
···
}
func (s *Signup) signup(w http.ResponseWriter, r *http.Request) {
-
if s.cf == nil {
-
http.Error(w, "signup is disabled", http.StatusFailedDependency)
-
}
-
emailId := r.FormValue("email")
+
switch r.Method {
+
case http.MethodGet:
+
s.pages.Signup(w)
+
case http.MethodPost:
+
if s.cf == nil {
+
http.Error(w, "signup is disabled", http.StatusFailedDependency)
+
}
+
emailId := r.FormValue("email")
-
if !email.IsValidEmail(emailId) {
-
s.pages.Notice(w, "login-msg", "Invalid email address.")
-
return
-
}
+
noticeId := "signup-msg"
+
if !email.IsValidEmail(emailId) {
+
s.pages.Notice(w, noticeId, "Invalid email address.")
+
return
+
}
-
exists, err := db.CheckEmailExistsAtAll(s.db, emailId)
-
if err != nil {
-
s.l.Error("failed to check email existence", "error", err)
-
s.pages.Notice(w, "login-msg", "Failed to complete signup. Try again later.")
-
return
-
}
-
if exists {
-
s.pages.Notice(w, "login-msg", "Email already exists.")
-
return
-
}
+
exists, err := db.CheckEmailExistsAtAll(s.db, emailId)
+
if err != nil {
+
s.l.Error("failed to check email existence", "error", err)
+
s.pages.Notice(w, noticeId, "Failed to complete signup. Try again later.")
+
return
+
}
+
if exists {
+
s.pages.Notice(w, noticeId, "Email already exists.")
+
return
+
}
-
code, err := s.inviteCodeRequest()
-
if err != nil {
-
s.l.Error("failed to create invite code", "error", err)
-
s.pages.Notice(w, "login-msg", "Failed to create invite code.")
-
return
-
}
+
code, err := s.inviteCodeRequest()
+
if err != nil {
+
s.l.Error("failed to create invite code", "error", err)
+
s.pages.Notice(w, noticeId, "Failed to create invite code.")
+
return
+
}
-
em := email.Email{
-
APIKey: s.config.Resend.ApiKey,
-
From: s.config.Resend.SentFrom,
-
To: emailId,
-
Subject: "Verify your Tangled account",
-
Text: `Copy and paste this code below to verify your account on Tangled.
+
em := email.Email{
+
APIKey: s.config.Resend.ApiKey,
+
From: s.config.Resend.SentFrom,
+
To: emailId,
+
Subject: "Verify your Tangled account",
+
Text: `Copy and paste this code below to verify your account on Tangled.
` + code,
-
Html: `<p>Copy and paste this code below to verify your account on Tangled.</p>
+
Html: `<p>Copy and paste this code below to verify your account on Tangled.</p>
<p><code>` + code + `</code></p>`,
-
}
+
}
-
err = email.SendEmail(em)
-
if err != nil {
-
s.l.Error("failed to send email", "error", err)
-
s.pages.Notice(w, "login-msg", "Failed to send email.")
-
return
-
}
-
err = db.AddInflightSignup(s.db, db.InflightSignup{
-
Email: emailId,
-
InviteCode: code,
-
})
-
if err != nil {
-
s.l.Error("failed to add inflight signup", "error", err)
-
s.pages.Notice(w, "login-msg", "Failed to complete sign up. Try again later.")
-
return
-
}
+
err = email.SendEmail(em)
+
if err != nil {
+
s.l.Error("failed to send email", "error", err)
+
s.pages.Notice(w, noticeId, "Failed to send email.")
+
return
+
}
+
err = db.AddInflightSignup(s.db, db.InflightSignup{
+
Email: emailId,
+
InviteCode: code,
+
})
+
if err != nil {
+
s.l.Error("failed to add inflight signup", "error", err)
+
s.pages.Notice(w, noticeId, "Failed to complete sign up. Try again later.")
+
return
+
}
-
s.pages.HxRedirect(w, "/signup/complete")
+
s.pages.HxRedirect(w, "/signup/complete")
+
}
}
func (s *Signup) complete(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
-
s.pages.CompleteSignup(w, pages.SignupParams{})
+
s.pages.CompleteSignup(w)
case http.MethodPost:
username := r.FormValue("username")
password := r.FormValue("password")