Skip to content

Commit

Permalink
Added support for a newsletter (#2517)
Browse files Browse the repository at this point in the history
In reference to idea:
alshedivat/al-folio#2097
In reference to request:
alshedivat/al-folio#923 (comment)

Added support to integrate a [loops.so](https://loops.so/) mailing list
into the site.

To use, you need to enable `newsletter` in `_config.yml`. You also must
specify a loops endpoint (although I think any mailing list endpoint can
work), which you can get when you set up a mailing list on loops. More
documentation on loops: [here](https://loops.so/docs/forms/custom-form).

Once that is enabled, the behavior is different depending on how you
specified your footer to behave in `_config.yml`. If `footer_fixed:
true`, then the sign up will appear at the bottom of the about page, as
well as at the bottom of blog posts, if you enable `related_posts`.

If `footer_fixed: false`, then the newsletter signup will be in the
footer (on every page), like it is in on [my
website](https://asboyer.com).

I'm not attached to the placement of the signup, and you can choose to
include it wherever you want with `{% include scripts/newsletter.liquid
%}`. Also if you include positional variables into that, you can choose
how you center the signup. So `{% include scripts/newsletter.liquid
left=true %}` positions the signup bar to the left.

Here are some screenshots below:
## Dark version

![image](https://github.com/alshedivat/al-folio/assets/52665298/af7fdb81-6e5f-47a9-958b-4cb93bba9e8f)

## Light version

![image](https://github.com/alshedivat/al-folio/assets/52665298/927f8bc5-b481-448b-ae5e-6f5b1c613243)
I think the input field color should probably change to maybe be light
for both themes? What do you think? I think the dark background looks
cool, but I don't usually see that done like that on other sites.

## Footer fixed

![image](https://github.com/alshedivat/al-folio/assets/52665298/c52f3dc1-0e45-400e-8b71-eeb00d00cb01)


![image](https://github.com/alshedivat/al-folio/assets/52665298/678a2d45-88ab-4d9a-b8cc-9fc6db26d744)

## Footer not fixed

![image](https://github.com/alshedivat/al-folio/assets/52665298/fd2c0228-2bce-4335-ac3c-5cb20a3307e2)


![image](https://github.com/alshedivat/al-folio/assets/52665298/f594b4f2-67e0-4f2b-a3e8-febd579aaf19)
To clarify, if footer isn't fixed, the email signup will appear on every
page.

---------

Co-authored-by: George <31376482+george-gca@users.noreply.github.com>
  • Loading branch information
asboyer and george-gca authored Jun 19, 2024
1 parent a467e04 commit 7472496
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 0 deletions.
9 changes: 9 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ external_sources:
- url: https://blog.google/technology/ai/google-gemini-update-flash-ai-assistant-io-2024/
published_date: 2024-05-14

# -----------------------------------------------------------------------------
# Newsletter
# -----------------------------------------------------------------------------

newsletter:
enabled: false
endpoint: # your loops endpoint (e.g., https://app.loops.so/api/newsletter-form/YOUR-ENDPOINT)
# https://loops.so/docs/forms/custom-form

# -----------------------------------------------------------------------------
# Collections
# -----------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions _includes/footer.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
</footer>
{% else %}
<footer class="sticky-bottom mt-5" role="contentinfo">
{% if site.newsletter.enabled %}
{% include scripts/newsletter.liquid %}
{% endif %}

<div class="container">
&copy; Copyright {{ site.time | date: '%Y' }}
{{ site.first_name }}
Expand Down
4 changes: 4 additions & 0 deletions _includes/related_posts.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@
<a class="text-pink-700 underline font-semibold hover:text-pink-800" href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
{% if site.newsletter.enabled and site.footer_fixed %}
<p class="mb-2" style="margin-top: 1.5rem !important">Subscribe to be notified of future articles:</p>
{% include scripts/newsletter.liquid left=true %}
{% endif %}
176 changes: 176 additions & 0 deletions _includes/scripts/newsletter.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<div
class="newsletter-form-container"
{% if include.center %}
style="margin: 20px"
{% endif %}
>
<form
class="newsletter-form"
action="https://app.loops.so/api/newsletter-form/{{ site.newsletter.endpoint }}"
method="POST"
style="justify-content: {% if include.left %}flex-start{% elsif include.right %}flex-end{% else %}center{% endif %}"
>
<input
class="newsletter-form-input"
name="newsletter-form-input"
type="email"
placeholder="user@example.com"
required=""
>

<button
type="submit"
class="newsletter-form-button"
style="justify-content: {% if include.left %}flex-start{% elsif include.right %}flex-end{% else %}center{% endif %}"
>
subscribe
</button>

<button
type="button"
class="newsletter-loading-button"
style="justify-content: {% if include.left %}flex-start{% elsif include.right %}flex-end{% else %}center{% endif %}"
>
Please wait...
</button>
</form>

<div
class="newsletter-success"
style="justify-content: {% if include.left %}flex-start{% elsif include.right %}flex-end{% else %}center{% endif %}"
>
<p class="newsletter-success-message">You're subscribed!</p>
</div>

<div
class="newsletter-error"
style="justify-content: {% if include.left %}flex-start{% elsif include.right %}flex-end{% else %}center{% endif %}"
>
<p class="newsletter-error-message">Oops! Something went wrong, please try again</p>
</div>

<button
class="newsletter-back-button"
type="button"
onmouseout='this.style.textDecoration="none"'
onmouseover='this.style.textDecoration="underline"'
>
&larr; Back
</button>
</div>

<script>
function submitHandler(event) {
event.preventDefault();
var container = event.target.parentNode;
var form = container.querySelector('.newsletter-form');
var formInput = container.querySelector('.newsletter-form-input');
var success = container.querySelector('.newsletter-success');
var errorContainer = container.querySelector('.newsletter-error');
var errorMessage = container.querySelector('.newsletter-error-message');
var backButton = container.querySelector('.newsletter-back-button');
var submitButton = container.querySelector('.newsletter-form-button');
var loadingButton = container.querySelector('.newsletter-loading-button');
const rateLimit = () => {
errorContainer.style.display = 'flex';
errorMessage.innerText = 'Too many signups, please try again in a little while';
submitButton.style.display = 'none';
formInput.style.display = 'none';
backButton.style.display = 'block';
};
// Compare current time with time of previous sign up
var time = new Date();
var timestamp = time.valueOf();
var previousTimestamp = localStorage.getItem('loops-form-timestamp');
// If last sign up was less than a minute ago
// display error
if (previousTimestamp && Number(previousTimestamp) + 60000 > timestamp) {
rateLimit();
return;
}
localStorage.setItem('loops-form-timestamp', timestamp);
submitButton.style.display = 'none';
loadingButton.style.display = 'flex';
var formBody = 'userGroup=&email=' + encodeURIComponent(formInput.value);
fetch(event.target.action, {
method: 'POST',
body: formBody,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then((res) => [res.ok, res.json(), res])
.then(([ok, dataPromise, res]) => {
if (ok) {
// If response successful
// display success
success.style.display = 'flex';
form.reset();
} else {
// If response unsuccessful
// display error message or response status
dataPromise.then((data) => {
errorContainer.style.display = 'flex';
errorMessage.innerText = data.message ? data.message : res.statusText;
});
}
})
.catch((error) => {
// check for cloudflare error
if (error.message === 'Failed to fetch') {
rateLimit();
return;
}
// If error caught
// display error message if available
errorContainer.style.display = 'flex';
if (error.message) errorMessage.innerText = error.message;
localStorage.setItem('loops-form-timestamp', '');
})
.finally(() => {
formInput.style.display = 'none';
loadingButton.style.display = 'none';
backButton.style.display = 'block';
});
}
function resetFormHandler(event) {
var container = event.target.parentNode;
var formInput = container.querySelector('.newsletter-form-input');
var success = container.querySelector('.newsletter-success');
var errorContainer = container.querySelector('.newsletter-error');
var errorMessage = container.querySelector('.newsletter-error-message');
var backButton = container.querySelector('.newsletter-back-button');
var submitButton = container.querySelector('.newsletter-form-button');
success.style.display = 'none';
errorContainer.style.display = 'none';
errorMessage.innerText = 'Oops! Something went wrong, please try again';
backButton.style.display = 'none';
formInput.style.display = 'flex';
submitButton.style.display = 'flex';
}
var formContainers = document.getElementsByClassName('newsletter-form-container');
for (var i = 0; i < formContainers.length; i++) {
var formContainer = formContainers[i];
var handlersAdded = formContainer.classList.contains('newsletter-handlers-added');
if (handlersAdded) continue;
formContainer.querySelector('.newsletter-form').addEventListener('submit', submitHandler);
formContainer.querySelector('.newsletter-back-button').addEventListener('click', resetFormHandler);
formContainer.classList.add('newsletter-handlers-added');
}
</script>

<noscript>
<style>
.newsletter-form-container {
display: none;
}
</style>
</noscript>
4 changes: 4 additions & 0 deletions _layouts/about.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,9 @@ layout: default
<div class="contact-note">{{ site.contact_note }}</div>
</div>
{% endif %}

{% if site.newsletter.enabled and site.footer_fixed %}
{% include scripts/newsletter.liquid center=true %}
{% endif %}
</article>
</div>
119 changes: 119 additions & 0 deletions _sass/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1128,13 +1128,127 @@ ninja-keys {
--ninja-modal-background: var(--global-bg-color);
--ninja-z-index: 1031;
}

ninja-keys::part(ninja-input) {
color: var(--ninja-selected-text-color);
}

ninja-keys::part(ninja-input-wrapper) {
background: var(--global-bg-color);
}

// newsletter
.newsletter-form-container {
margin-bottom: 20px;
}

.newsletter-form {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}

.newsletter-form-input {
color: var(--global-newsletter-text-color);
background: var(--global-newsletter-bg-color);
border: 1px solid var(--global-newsletter-text-color);
outline: none;
margin: 0px 10px 0px 0px;
width: 100%;
max-width: 350px;
min-width: 100px;
box-sizing: border-box;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 2px;
border-radius: 6px;
padding: 8px 12px;
}

.newsletter-form-input:focus {
border-color: var(--global-theme-color) !important;
}

.newsletter-form-button {
background: var(--global-theme-color);
color: var(--global-bg-color);
display: flex;
width: min-content;
max-width: 200px;
white-space: nowrap;
height: 38px;
align-items: center;
flex-direction: row;
padding: 9px 17px;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 2px;
border-radius: 6px;
text-align: center;
font-style: normal;
font-weight: 500;
line-height: 20px;
border: none;
cursor: pointer;
}

.newsletter-loading-button {
background: var(--global-theme-color);
color: var(--global-bg-color);
display: none;
width: min-content;
max-width: 300px;
white-space: nowrap;
height: 38px;
align-items: center;
flex-direction: row;
padding: 9px 17px;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 2px;
border-radius: 6px;
text-align: center;
font-style: normal;
font-weight: 500;
line-height: 20px;
border: none;
cursor: pointer;
margin-right: 20px;
}

.newsletter-success {
color: var(--global-text-color);
display: none;
align-items: center;
width: 100%;
}

.newsletter-error {
color: var(--global-theme-color);
display: none;
align-items: center;
width: 100%;
}

.newsletter-back-button {
color: var(--global-theme-color);
margin: 10px auto;
text-align: center;
display: none;
background: transparent;
border: none;
cursor: pointer;
}

@media (max-width: 575px) {
.newsletter-form-input,
.newsletter-form-button,
.newsletter-loading-button,
.newsletter-success,
.newsletter-error {
font-size: 16px !important;
}
.newsletter-form-container {
margin-right: 20px;
margin-left: 20px;
}
}

// popover is used for annotation in bib.liquid
.popover {
background-color: var(--global-bg-color);
Expand All @@ -1144,25 +1258,30 @@ ninja-keys::part(ninja-input-wrapper) {
color: var(--global-text-color); // Header text color
border-bottom: 1px solid var(--global-divider-color);
}

.popover-body {
color: var(--global-text-color); // Body text color
}
}

.bs-popover-top {
// arrow fill color
.arrow::after {
border-top-color: var(--global-bg-color);
}

// arrow border color
.arrow:before {
border-top-color: var(--global-divider-color);
}
}

.bs-popover-bottom {
// arrow fill color
.arrow::after {
border-bottom-color: var(--global-bg-color);
}

// arrow border color
.arrow:before {
border-bottom-color: var(--global-divider-color);
Expand Down
4 changes: 4 additions & 0 deletions _sass/_themes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
--global-highlight-color: #{$red-color-dark};
--global-back-to-top-bg-color: rgba(#{red($black-color)}, #{green($black-color)}, #{blue($black-color)}, 0.4);
--global-back-to-top-text-color: #{$white-color};
--global-newsletter-bg-color: #{$white-color};
--global-newsletter-text-color: #{$black-color};

--global-tip-block: #42b983;
--global-tip-block-bg: #e2f5ec;
Expand Down Expand Up @@ -81,6 +83,8 @@ html[data-theme="dark"] {
--global-card-bg-color: #{$grey-900};
--global-back-to-top-bg-color: rgba(#{red($white-color)}, #{green($white-color)}, #{blue($white-color)}, 0.5);
--global-back-to-top-text-color: #{$black-color};
--global-newsletter-bg-color: #{$grey-color-light};
--global-newsletter-text-color: #{$grey-color-dark};

--global-tip-block: #42b983;
--global-tip-block-bg: #e2f5ec;
Expand Down

0 comments on commit 7472496

Please sign in to comment.