1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-07-23 01:11:16 -04:00

contact form cleanup

This commit is contained in:
2021-12-05 18:28:33 -05:00
parent aebc982dc3
commit b4cd9eea59
9 changed files with 63 additions and 56 deletions

View File

@@ -4,6 +4,7 @@
"rules": { "rules": {
"color-hex-length": "long", "color-hex-length": "long",
"max-nesting-depth": 6, "max-nesting-depth": 6,
"no-descending-specificity": null,
"order/order": null, "order/order": null,
"order/properties-alphabetical-order": null, "order/properties-alphabetical-order": null,
"plugin/no-unsupported-browser-features": [true, { "severity": "warning", "ignore": ["flexbox"] }], "plugin/no-unsupported-browser-features": [true, { "severity": "warning", "ignore": ["flexbox"] }],

View File

@@ -2,16 +2,17 @@ import "vanilla-hcaptcha";
import { h, render } from "preact"; import { h, render } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import fetch from "unfetch"; import fetch from "unfetch";
import parseEmoji from "./emoji.js";
const CONTACT_ENDPOINT = "/api/contact/"; const CONTACT_ENDPOINT = "/api/contact/";
const ContactForm = () => { const ContactForm = () => {
// status/feedback: // status/feedback:
const [status, setStatus] = useState({ success: false, action: "Submit", message: "" }); const [status, setStatus] = useState({ success: false, message: "" });
// keep track of fetch: // keep track of fetch:
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const onSubmit = async (e) => { const onSubmit = (e) => {
// immediately prevent browser from actually navigating to a new page // immediately prevent browser from actually navigating to a new page
e.preventDefault(); e.preventDefault();
@@ -19,19 +20,18 @@ const ContactForm = () => {
setSending(true); setSending(true);
// extract data from form fields // extract data from form fields
const { name, email, message } = e.target.elements;
const formData = { const formData = {
name: name.value, name: e.target.elements.name?.value,
email: email.value, email: e.target.elements.email?.value,
message: message.value, message: e.target.elements.message?.value,
"h-captcha-response": e.target.elements["h-captcha-response"].value, "h-captcha-response": e.target.elements["h-captcha-response"]?.value,
}; };
// some client-side validation. these are all also checked on the server to be safe but we can save some // some client-side validation to save requests (these are also checked on the server to be safe)
// unnecessary requests here. // TODO: change border color of the specific empty/missing field(s) to red
if (!(formData.name && formData.email && formData.message && formData["h-captcha-response"])) { if (!(formData.name && formData.email && formData.message && formData["h-captcha-response"])) {
setSending(false); setSending(false);
setStatus({ success: false, action: "Try Again", message: "Please make sure that all fields are filled in." }); setStatus({ success: false, message: "Please make sure that all fields are filled in." });
// remove focus from the submit button // remove focus from the submit button
document.activeElement.blur(); document.activeElement.blur();
@@ -55,7 +55,7 @@ const ContactForm = () => {
if (data.success === true) { if (data.success === true) {
// handle successful submission // handle successful submission
// disable submissions, hide the send button, and let user know we were successful // disable submissions, hide the send button, and let user know we were successful
setStatus({ success: true, action: "", message: "Success! You should hear from me soon. :)" }); setStatus({ success: true, message: "Thanks! You should hear from me soon. 😊" });
} else { } else {
// pass on any error sent by the server // pass on any error sent by the server
throw new Error(data.message); throw new Error(data.message);
@@ -70,18 +70,16 @@ const ContactForm = () => {
if (message === "USER_INVALID_CAPTCHA") { if (message === "USER_INVALID_CAPTCHA") {
setStatus({ setStatus({
success: false, success: false,
action: "Try Again", message: "❗ Did you complete the CAPTCHA? (If you're human, that is...)",
message: "Did you complete the CAPTCHA? (If you're human, that is...)",
}); });
} else if (message === "USER_MISSING_DATA") { } else if (message === "USER_MISSING_DATA") {
setStatus({ setStatus({
success: false, success: false,
action: "Try Again", message: "❗ Please make sure that all fields are filled in.",
message: "Please make sure that all fields are filled in.",
}); });
} else { } else {
// something else went wrong, and it's probably my fault... // something else went wrong, and it's probably my fault...
setStatus({ success: false, action: "Try Again", message: "Internal server error. Try again later?" }); setStatus({ success: false, message: "Internal server error. Try again later?" });
} }
// remove focus from the submit button // remove focus from the submit button
@@ -95,7 +93,7 @@ const ContactForm = () => {
<input type="email" name="email" placeholder="Email" disabled={status.success} /> <input type="email" name="email" placeholder="Email" disabled={status.success} />
<textarea name="message" placeholder="Write something..." disabled={status.success} /> <textarea name="message" placeholder="Write something..." disabled={status.success} />
<span id="contact-form-md-info"> <div id="contact-form-md-info">
Basic{" "} Basic{" "}
<a <a
href="https://commonmark.org/help/" href="https://commonmark.org/help/"
@@ -110,27 +108,29 @@ const ContactForm = () => {
links links
</a> </a>
](https://jarv.is), and <code>`code`</code>. ](https://jarv.is), and <code>`code`</code>.
</span> </div>
<h-captcha id="contact-form-captcha" site-key={process.env.HCAPTCHA_SITE_KEY} size="normal" tabindex="0" /> <div id="contact-form-captcha">
<h-captcha site-key={process.env.HCAPTCHA_SITE_KEY} size="normal" />
</div>
<div id="contact-form-action-row"> <div id="contact-form-action-row">
<button <button
id="contact-form-btn-submit" id="contact-form-btn-submit"
title={status.action} title="Send Message"
aria-label={status.action} aria-label="Send Message"
disabled={sending} disabled={sending}
style={{ display: status.success ? "none" : null }} style={{ display: status.success ? "none" : null }}
> // eslint-disable-next-line react/no-danger
{sending ? "Sending..." : status.action} dangerouslySetInnerHTML={{ __html: parseEmoji(sending ? "Sending..." : "📤 Send") }}
</button> />
<span <span
class="contact-form-result" class="contact-form-result"
id={status.success ? "contact-form-result-success" : "contact-form-result-error"} id={status.success ? "contact-form-result-success" : "contact-form-result-error"}
> // eslint-disable-next-line react/no-danger
{status.message} dangerouslySetInnerHTML={{ __html: parseEmoji(status.message) }}
</span> />
</div> </div>
</form> </form>
); );

View File

@@ -38,12 +38,12 @@
30% { 30% {
transform: rotate(0deg); transform: rotate(0deg);
} }
// stylelint-enable rule-empty-line-before
// pause for 3.5 out of 5 seconds // pause for 3.5 out of 5 seconds
100% { 100% {
transform: rotate(0deg); transform: rotate(0deg);
} }
// stylelint-enable rule-empty-line-before
} }
@keyframes beat { @keyframes beat {
@@ -63,12 +63,12 @@
8% { 8% {
transform: scale(1); transform: scale(1);
} }
// stylelint-enable rule-empty-line-before
// pause for ~9 out of 10 seconds // pause for ~9 out of 10 seconds
100% { 100% {
transform: scale(1); transform: scale(1);
} }
// stylelint-enable rule-empty-line-before
} }
// modified from https://tobiasahlin.com/spinkit/ // modified from https://tobiasahlin.com/spinkit/

View File

@@ -50,9 +50,7 @@ div#content {
} }
// AnchorJS styles // AnchorJS styles
// stylelint-disable-next-line no-descending-specificity
a.anchorjs-link { a.anchorjs-link {
display: inline-block;
margin-left: 0.25em; margin-left: 0.25em;
padding: 0 0.5em 0 0.25em; padding: 0 0.5em 0 0.25em;
background: none; background: none;
@@ -70,7 +68,6 @@ div#content {
) )
); );
// stylelint-disable-next-line no-descending-specificity
&:hover { &:hover {
@include themes.themed( @include themes.themed(
( (

View File

@@ -127,7 +127,6 @@ div.highlight-clipboard-enabled {
// Syntax Highlighting (light) - modified from Monokai Light: https://github.com/mlgill/pygments-style-monokailight // Syntax Highlighting (light) - modified from Monokai Light: https://github.com/mlgill/pygments-style-monokailight
body.light { body.light {
// stylelint-disable no-descending-specificity
div.highlight, div.highlight,
button.copy-button, button.copy-button,
:not(pre) > code { :not(pre) > code {
@@ -139,7 +138,6 @@ body.light {
button.copy-button { button.copy-button {
color: #313131; color: #313131;
} }
// stylelint-enable no-descending-specificity
.chroma { .chroma {
.k, .k,
@@ -215,7 +213,6 @@ body.light {
// Syntax Highlighting (dark) - modified from Dracula: https://github.com/dracula/pygments // Syntax Highlighting (dark) - modified from Dracula: https://github.com/dracula/pygments
body.dark { body.dark {
// stylelint-disable no-descending-specificity
div.highlight, div.highlight,
button.copy-button, button.copy-button,
:not(pre) > code { :not(pre) > code {
@@ -227,7 +224,6 @@ body.dark {
button.copy-button { button.copy-button {
color: #e4e4e4; color: #e4e4e4;
} }
// stylelint-enable no-descending-specificity
.chroma { .chroma {
.k, .k,

View File

@@ -10,11 +10,6 @@ div.layout-contact {
text-align: center; text-align: center;
} }
p {
font-size: 0.9em;
margin-bottom: 0.5em;
}
code { code {
background: none !important; background: none !important;
border: 0; border: 0;
@@ -30,7 +25,7 @@ div.layout-contact {
width: 100%; width: 100%;
padding: 0.8em; padding: 0.8em;
margin: 0.6em 0; margin: 0.6em 0;
border: 1px solid; border: 2px solid;
border-radius: 0.3em; border-radius: 0.3em;
font-size: 0.9em; font-size: 0.9em;
@@ -41,6 +36,16 @@ div.layout-contact {
border-color: "light", border-color: "light",
) )
); );
&:focus {
outline: none; // disable browsers' outer border
@include themes.themed(
(
border-color: "links",
)
);
}
} }
textarea { textarea {
@@ -54,8 +59,10 @@ div.layout-contact {
div#contact-form-action-row { div#contact-form-action-row {
display: flex; display: flex;
align-items: center; align-items: center;
min-height: 3.75em;
button { button {
flex-shrink: 0;
padding: 0.8em 1.2em; padding: 0.8em 1.2em;
margin-right: 1.5em; margin-right: 1.5em;
border: 0; border: 0;
@@ -71,8 +78,7 @@ div.layout-contact {
) )
); );
&:hover, &:hover {
&:focus {
@include themes.themed( @include themes.themed(
( (
color: "super-duper-light", color: "super-duper-light",
@@ -82,13 +88,14 @@ div.layout-contact {
} }
img.emoji { img.emoji {
margin-left: 0;
margin-right: 0.4em; margin-right: 0.4em;
cursor: inherit; cursor: inherit;
} }
} }
span.contact-form-result { span.contact-form-result {
font-size: 0.9em; font-size: 0.925em;
font-weight: 600; font-weight: 600;
&#contact-form-result-success { &#contact-form-result-success {
@@ -110,16 +117,14 @@ div.layout-contact {
} }
// hcaptcha widget // hcaptcha widget
#contact-form-captcha { div#contact-form-captcha {
display: block; margin: 1em 0;
margin: 1.2em 0;
} }
span#contact-form-md-info { div#contact-form-md-info {
display: block;
font-size: 0.75em; font-size: 0.75em;
margin-top: 0.25em; line-height: 1.75;
margin-left: 0.75em; margin: 0.2em 0 0.6em 0;
a { a {
// disable fancy underline without `.no-underline` // disable fancy underline without `.no-underline`

View File

@@ -18,8 +18,7 @@ div.layout-list {
ul { ul {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
padding-left: 0; padding: 0;
display: block;
} }
li { li {

View File

@@ -6,3 +6,11 @@ sitemap:
changefreq: never changefreq: never
priority: 0.0 priority: 0.0
--- ---
<!-- markdownlint-disable -->
Fill out this quick form and I'll get back to you as soon as I can! You can also <a href="&#x6D;&#x61;&#x69;&#x6C;&#x74;&#x6F;&#x3A;&#x6A;&#x61;&#x6B;&#x65;&#x40;&#x6A;&#x61;&#x72;&#x76;&#x2E;&#x69;&#x73;">email me directly</a>, send me a <a href="https://twitter.com/messages/compose?recipient_id=229769022" target="_blank" rel="noopener nofollow">direct message on Twitter</a>, or <a href="sms:+1-617-917-3737">text me</a>.
🔐 You can grab my public key here: <a href="/pubkey.asc" title="My Public PGP Key" target="_blank" rel="pgpkey authn noopener"><code>6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39</code></a>.
<!-- markdownlint-enable -->

View File

@@ -2,8 +2,9 @@
<div class="layout layout-contact"> <div class="layout layout-contact">
<h1><a href="{{ .Permalink }}">{{ .Title | markdownify }}</a></h1> <h1><a href="{{ .Permalink }}">{{ .Title | markdownify }}</a></h1>
<p>Fill out this quick form and I'll get back to you as soon as I can! You can also <a href="&#x6D;&#x61;&#x69;&#x6C;&#x74;&#x6F;&#x3A;&#x6A;&#x61;&#x6B;&#x65;&#x40;&#x6A;&#x61;&#x72;&#x76;&#x2E;&#x69;&#x73;">email me directly</a>, send me a <a href="https://twitter.com/messages/compose?recipient_id=229769022" target="_blank" rel="noopener nofollow">direct message on Twitter</a>, or <a href="sms:+1-617-917-3737">text me</a>.</p> <div id="content">
<p>🔐 You can grab my public key here: <a href="/pubkey.asc" title="My Public PGP Key" target="_blank" rel="pgpkey authn noopener"><code>6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39</code></a>.</p> {{ .Content }}
</div>
<div id="contact-form-wrapper"></div> <div id="contact-form-wrapper"></div>
</div> </div>