Time machine submission form

This commit is contained in:
Asaf Gartner 2023-06-02 00:42:46 +03:00
parent 2d61286831
commit dcdbc67b6c
9 changed files with 307 additions and 120 deletions

View File

@ -91,6 +91,41 @@ func SendPasswordReset(toAddress string, toName string, username string, resetTo
return nil
}
type TimeMachineEmailData struct {
ProfileUrl string
Username string
UserEmail string
DiscordUsername string
MediaUrl string
DeviceInfo string
Description string
}
func SendTimeMachineEmail(profileUrl, username, userEmail, discordUsername, mediaUrl, deviceInfo, description string, perf *perf.RequestPerf) error {
perf.StartBlock("EMAIL", "Time machine email")
defer perf.EndBlock()
contents, err := renderTemplate("email_time_machine.html", TimeMachineEmailData{
ProfileUrl: profileUrl,
Username: username,
UserEmail: userEmail,
DiscordUsername: discordUsername,
MediaUrl: mediaUrl,
DeviceInfo: deviceInfo,
Description: description,
})
if err != nil {
return err
}
err = sendMail("team@handmade.network", "HMN Team", "[time machine] New submission", contents)
if err != nil {
return oops.New(err, "Failed to send email")
}
return nil
}
var EmailRegex = regexp.MustCompile(`^[^:\p{Cc} ]+@[^:\p{Cc} ]+\.[^:\p{Cc} ]+$`)
func IsEmail(address string) bool {

View File

@ -112,6 +112,20 @@ func BuildTimeMachine() string {
return Url("/timemachine", nil)
}
var RegexTimeMachineForm = regexp.MustCompile("^/timemachine/submit$")
func BuildTimeMachineForm() string {
defer CatchPanic()
return Url("/timemachine/submit", nil)
}
var RegexTimeMachineFormDone = regexp.MustCompile("^/timemachine/thanks$")
func BuildTimeMachineFormDone() string {
defer CatchPanic()
return Url("/timemachine/thanks", nil)
}
// QUESTION(ben): Can we change these routes?
var RegexLoginAction = regexp.MustCompile("^/login$")

View File

@ -0,0 +1,6 @@
<p>Submission by <b><a href="{{ .ProfileUrl }}">{{ .Username }}</a></b> ({{ .UserEmail }}) {{ if .DiscordUsername }} On Discord: {{ .DiscordUsername }} {{ end }}</p>
<p>Submitted url: <a href="{{ .MediaUrl }}">{{ .MediaUrl }}</a></p>
Device info:<br />
<pre>{{ .DeviceInfo }}</pre>
Description:<br />
<pre>{{ .Description }}</pre>

View File

@ -33,6 +33,127 @@
<link href='https://fonts.googleapis.com/css?family=Fira+Mono:300,400,500,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="{{ static "style.css" }}">
<style>
body {
/*
This is too light for accessiblity imo. I'll darken them a bit and see how Ben feels
about it. -- Jake 5/26/2023
background: linear-gradient(15deg, #b4c4fe, #eb8cbf ); */
background: linear-gradient(15deg, #7391fd, #e25ca4);
}
.user-options,
header form,
header .menu-bar .wiki,
header .menu-bar .library {
display: none !important;
}
header {
border-bottom-color: white;
margin-bottom: 0 !important;
}
.hmn-logo {
background-color: rgba(255, 255, 255, 0.1) !important;
}
header a,
footer a {
color: white !important;
}
header .submenu {
background-color: #d661ad;
}
#logo {
width: 16rem;
}
h1,
h2,
h3 {
font-family: "MohaveHMN", sans-serif;
margin-bottom: 0;
font-weight: normal;
}
/* '95-esque styles */
.frame {
width: 100%;
background: #c3c3c3;
border-width: 2px;
border-style: solid;
border-color: #fff #333 #333 #fff;
border-image: url('{{ dataimg "timemachine/win95-border.gif" }}') 4;
padding: 1px;
color: black;
}
.frame ol,
.frame ul {
padding-left: 5px;
}
.frame ul {
list-style: disc;
}
.frame strong {
font-weight: bold;
}
.frame .title {
padding: 0 0.1875rem; /* 0 3px */
height: 1.125rem; /* 18px */
line-height: 1.125rem; /* 18px */
font-size: 0.8125rem; /* 13px */
width: 100%;
background: rgb(0, 0, 130);
color: #fff;
position: relative;
}
.frame .frame-close {
width: 1rem; /* 16px */
height: 0.875rem; /* 14px */
position: absolute;
right: 0.125rem; /* 2px */
top: 0.125rem; /* 2px */
cursor: pointer;
}
.frame * {
font-family: "MS Sans Serif", "Microsoft Sans Serif", sans-serif;
}
.inset {
border-width: 1px;
border-color: #828282 #ffffff #ffffff #828282;
border-style: solid;
border-image: url('{{ dataimg "timemachine/win95-border-inset.gif" }}') 2;
}
img.pixelated {
image-rendering: crisp-edges;
}
.drop-shadow {
box-shadow: 2px 2px black;
}
.win95-btn {
width: 5.6rem;
border-width: 2px;
border-style: solid;
border-color: #fff #333 #333 #fff;
border-image: url('{{ dataimg "timemachine/win95-border-btn.gif" }}') 4;
text-align: center;
cursor: pointer;
}
</style>
</head>
<body>

View File

@ -8,126 +8,6 @@
{{ end }}
{{ define "content" }}
<style>
body {
/*
This is too light for accessiblity imo. I'll darken them a bit and see how Ben feels
about it. -- Jake 5/26/2023
background: linear-gradient(15deg, #b4c4fe, #eb8cbf ); */
background: linear-gradient(15deg, #7391fd, #e25ca4);
}
.user-options,
header form,
header .menu-bar .wiki,
header .menu-bar .library {
display: none !important;
}
header {
border-bottom-color: white;
margin-bottom: 0 !important;
}
.hmn-logo {
background-color: rgba(255, 255, 255, 0.1) !important;
}
header a,
footer a {
color: white !important;
}
header .submenu {
background-color: #d661ad;
}
#logo {
width: 16rem;
}
h1,
h2,
h3 {
font-family: "MohaveHMN", sans-serif;
margin-bottom: 0;
font-weight: normal;
}
/* '95-esque styles */
.frame {
width: 100%;
background: #c3c3c3;
border-width: 2px;
border-style: solid;
border-color: #fff #333 #333 #fff;
border-image: url('{{ dataimg "timemachine/win95-border.gif" }}') 4;
padding: 1px;
color: black;
}
.frame ol,
.frame ul {
padding-left: 5px;
}
.frame ul {
list-style: disc;
}
.frame strong {
font-weight: bold;
}
.frame .title {
padding: 0 0.1875rem; /* 0 3px */
height: 1.125rem; /* 18px */
line-height: 1.125rem; /* 18px */
font-size: 0.8125rem; /* 13px */
width: 100%;
background: rgb(0, 0, 130);
color: #fff;
position: relative;
}
.frame .frame-close {
width: 1rem; /* 16px */
height: 0.875rem; /* 14px */
position: absolute;
right: 0.125rem; /* 2px */
top: 0.125rem; /* 2px */
cursor: pointer;
}
.frame * {
font-family: "MS Sans Serif", "Microsoft Sans Serif", sans-serif;
}
.inset {
border-width: 1px;
border-color: #828282 #ffffff #ffffff #828282;
border-style: solid;
border-image: url('{{ dataimg "timemachine/win95-border-inset.gif" }}') 2;
}
img.pixelated {
image-rendering: crisp-edges;
}
.drop-shadow {
box-shadow: 2px 2px black;
}
.win95-btn {
width: 5.6rem;
border-width: 2px;
border-style: solid;
border-color: #fff #333 #333 #fff;
border-image: url('{{ dataimg "timemachine/win95-border-btn.gif" }}') 4;
text-align: center;
cursor: pointer;
}
</style>
<div class="center-layout content mw7 ph3 flex flex-column g3">
<div>

View File

@ -0,0 +1,48 @@
{{ template "timemachine_base.html" . }}
{{ define "frame title" }}
<div class="title">
{{ . }}
<img class="frame-close" src="{{ dataimg "timemachine/win95-close.gif" }}">
</div>
{{ end }}
{{ define "content" }}
<div class="center-layout content mw7 ph3 flex flex-column g3">
<div class="frame mv4">
{{ template "frame title" "Hello" }}
<form class="post-content" action="" method="POST">
{{ csrftoken .Session }}
<input type="url" name="media_url" required />
<textarea name="device_info" required></textarea>
<textarea name="description" required></textarea>
<input class="win95-btn" type="submit" value="Submit" />
</form>
</div>
</div>
<script>
let form = document.querySelector("form");
let mediaUrl = document.querySelector("[name=media_url]");
let deviceInfo = document.querySelector("[name=device_info]");
let description = document.querySelector("[name=description]");
let submitBtn = document.querySelector("[type=submit]");
function saveData() {
localStorage.setItem("tm_media_url", mediaUrl.value);
localStorage.setItem("tm_device_info", deviceInfo.value);
localStorage.setItem("tm_description", description.value);
}
form.addEventListener("submit", function() {
saveData();
submitBtn.disabled = true;
});
document.addEventListener("visibilitychange", function() {
saveData();
});
mediaUrl.value = localStorage.getItem("tm_media_url") ?? "";
deviceInfo.value = localStorage.getItem("tm_device_info") ?? "";
description.value = localStorage.getItem("tm_description") ?? "";
</script>
{{ end }}

View File

@ -0,0 +1,21 @@
{{ template "timemachine_base.html" . }}
{{ define "frame title" }}
<div class="title">
{{ . }}
<img class="frame-close" src="{{ dataimg "timemachine/win95-close.gif" }}">
</div>
{{ end }}
{{ define "content" }}
<div class="center-layout content mw7 ph3 flex flex-column g3">
<div class="frame">
Thank you for your submission
</div>
</div>
<script>
localStorage.removeItem("tm_media_url");
localStorage.removeItem("tm_device_info");
localStorage.removeItem("tm_description");
</script>
{{ end }}

View File

@ -68,6 +68,9 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexJamRecap2023_Visibility, JamRecap2023_Visibility)
hmnOnly.GET(hmnurl.RegexTimeMachine, TimeMachine)
hmnOnly.GET(hmnurl.RegexTimeMachineForm, needsAuth(TimeMachineForm))
hmnOnly.GET(hmnurl.RegexTimeMachineFormDone, needsAuth(TimeMachineFormDone))
hmnOnly.POST(hmnurl.RegexTimeMachineForm, needsAuth(csrfMiddleware(TimeMachineFormSubmit)))
hmnOnly.GET(hmnurl.RegexStaffRolesIndex, StaffRolesIndex)
hmnOnly.GET(hmnurl.RegexStaffRole, StaffRole)

View File

@ -0,0 +1,59 @@
package website
import (
"net/http"
"strings"
"git.handmade.network/hmn/hmn/src/email"
"git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/oops"
)
func TimeMachineForm(c *RequestContext) ResponseData {
var res ResponseData
res.MustWriteTemplate(
"time_machine_form.html",
getBaseDataAutocrumb(c, "Time Machine"),
c.Perf,
)
return res
}
func TimeMachineFormSubmit(c *RequestContext) ResponseData {
c.Req.ParseForm()
mediaUrl := strings.TrimSpace(c.Req.Form.Get("media_url"))
deviceInfo := strings.TrimSpace(c.Req.Form.Get("device_info"))
description := strings.TrimSpace(c.Req.Form.Get("description"))
discordUsername := ""
if c.CurrentUser.DiscordUser != nil {
discordUsername = c.CurrentUser.DiscordUser.Username
}
err := email.SendTimeMachineEmail(
hmnurl.BuildUserProfile(c.CurrentUser.Username),
c.CurrentUser.BestName(),
c.CurrentUser.Email,
discordUsername,
mediaUrl,
deviceInfo,
description,
c.Perf,
)
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to send time machine email"))
}
return c.Redirect(hmnurl.BuildTimeMachineFormDone(), http.StatusSeeOther)
}
func TimeMachineFormDone(c *RequestContext) ResponseData {
var res ResponseData
res.MustWriteTemplate(
"time_machine_form_done.html",
getBaseDataAutocrumb(c, "Time Machine"),
c.Perf,
)
return res
}