Skip to content

Commit

Permalink
#134 - Block gmail addresses from newsletter (#144)
Browse files Browse the repository at this point in the history
* implement email validation logic

* display validation-error when fails

* remove useless layout css utils / create ModalTemplate.vue

* basic nanostores setup for modal-system of the website

* style modals

* hook up modal-system to ModalTemplate.vue

* make sure form submitted on validation pass
  • Loading branch information
SebinSong authored Aug 29, 2024
1 parent ab8a06f commit 2496513
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 108 deletions.
85 changes: 74 additions & 11 deletions src/components/Footer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@
<footer class="c-footer">
<div class="c-top">
<div class="c-wrapper"><img class="c-logo-img" v-src="'/images/group-income-icon-transparent.png'" />
<form class="c-form"
<form class="c-form" ref="emailForm"
action="https://buttondown.email/api/emails/embed-subscribe/okturtles"
method="post"
target="popupwindow"
@submit="onFormSubmit">
@submit.prevent="onFormSubmit">
<h4 class="is-title-6">STAY UP TO DATE</h4>
<p>Subscribe to our newsletter to be the first to know when the prototype is ready.</p>
<div class="c-input">
<input class="input" type="email" name="email" id="bd-email" placeholder="Your email address" />
<button class="is-unstyled c-send-btn" type="submit" value="Subscribe">
<i class="icon-paper-plane"></i>
</button>
</div>

<fieldset class="c-mail-form-field">
<div class="c-input">
<input :class="['input', { error: emailErr }]"
type="email" name="email" id="bd-email"
placeholder="Your email address"
v-model.trim="email" />
<button class="is-unstyled c-send-btn" type="submit" value="Subscribe">
<i class="icon-paper-plane"></i>
</button>
</div>

<p v-if="emailErr" class="error c-email-err">{{ emailErr }}</p>
</fieldset>
</form>
</div>
<div class="c-links">
Expand Down Expand Up @@ -49,22 +57,66 @@
</div>
<p class="copyright">{{ copyRightText }}</p>
</div>
</footer>

<NewsLetterWarningModal />
</footer>
</template>

<script>
import NewsLetterWarningModal from '@/components/modals/NewsLetterWarningModal.vue'
import { validateEmail } from '@/utils/helpers.js'
import { openModal } from '@/store'
const EMAIL_BLACKLIST = [
'gmail.com',
'googlemail.com',
'google.com'
]
export default {
name: 'Footer',
components: {
NewsLetterWarningModal
},
data () {
return {
email: '',
emailErr: '',
isSubmitting: false
}
},
computed: {
copyRightText () {
const thisYear = new Date().getFullYear()
return `Copyright © 2015-${thisYear} okTurtles Foundation`
}
},
methods: {
validateEmailField () {
const getAddressSegment = str => str.split('@')[1]
let passed = true
if (!validateEmail(this.email)) {
this.emailErr = 'Please enter correct email format.'
passed = false
} else if (EMAIL_BLACKLIST.includes(getAddressSegment(this.email))) {
openModal('NewsLetterWarningModal')
passed = false
}
return passed
},
onFormSubmit () {
window.open('https://buttondown.email/okturtles', 'popupwindow')
if (this.validateEmailField()) {
this.$refs.emailForm.submit()
this.email = ''
}
}
},
watch: {
email () {
if (this.emailErr) {
this.emailErr = ''
}
}
}
}
Expand Down Expand Up @@ -118,16 +170,27 @@ export default {
}
}
.c-input {
.c-mail-form-field {
position: relative;
margin: 1.375rem 0;
}
.c-input {
position: relative;
input {
padding-right: 2.75rem;
border-radius: 0.5rem;
}
}
.c-email-err {
position: relative;
width: 100%;
padding-left: 2px;
}
.c-send-btn {
position: absolute;
display: inline-flex;
Expand Down
162 changes: 162 additions & 0 deletions src/components/modals/ModalTemplate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<template>
<div v-if="$isModalOpen(modalName)" class="l-modal"
v-bind="containerAttrs">
<div class="c-modal-background"
@click="close"></div>

<div class="c-modal-content">
<header class="c-modal-header">
<h1 v-if="modalTitle">{{ modalTitle }}</h1>

<button class="is-icon has-background-inverted c-modal-close"
aria-label="Close modal"
@click.stop="close">
<i class="icon-times"></i>
</button>
</header>

<section class="c-modal-body">
<slot></slot>
</section>

<footer v-if="$slots.footer" class="c-modal-footer">
<slot name="footer"></slot>
</footer>
</div>
</div>
</template>
<script setup lang="ts">
import { useStore } from '@nanostores/vue';
import { isModalOpen, closeModal } from '@/store'
// props
interface ComponentProps {
modalName: string,
modalTitle?: string
}
const props = defineProps<ComponentProps>()
// local-state
const $isModalOpen = useStore(isModalOpen)
const containerAttrs = {
role: 'dialog',
ariaLabel: props.modalTitle || 'dialog'
}
// methods
const close = () => {
closeModal(props.modalName)
}
defineExpose({ close })
</script>

<style lang="scss" scoped>
@import "../../styles/_variables";
.c-modal-background {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(10, 10, 10, 0.86);
opacity: 0;
animation: modal-bg-in 250ms ease-out forwards;
}
.c-modal-content {
position: relative;
border-radius: 0.375rem;
width: calc(100vw - 3rem);
max-width: 40rem;
height: auto;
max-height: calc(100% - 5rem);
overflow: hidden;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"m-header"
"m-body"
"m-footer";
background-color: $background_0;
color: $text_0;
text-align: left;
opacity: 0;
animation: modal-content-in 300ms ease-out forwards;
animation-delay: 100ms;
}
.c-modal-header {
grid-area: m-header;
display: flex;
align-items: flex-start;
justify-content: flex-start;
column-gap: 1rem;
padding: 1.5rem 1.25rem;
background-color: var(--general_2);
h1 {
font-family: "Poppins";
font-size: 1.85rem;
font-weight: 600;
width: 100%;
text-align: left;
flex-grow: 1;
line-height: 1.5;
margin-top: 0.5rem;
}
button.c-modal-close {
flex-shrink: 0;
}
@include tablet {
flex-direction: column-reverse;
column-gap: 0;
align-items: stretch;
h1 {
font-size: 2.25rem;
text-align: center;
margin-top: 0;
}
button.c-modal-close {
align-self: flex-end;
}
}
}
.c-modal-body {
grid-area: m-body;
padding: 2rem 1.25rem;
overflow: auto;
@include tablet {
padding: 2.5rem 1.75rem;
}
}
.c-modal-footer {
grid-area: m-footer;
}
@keyframes modal-bg-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes modal-content-in {
0% {
opacity: 0;
transform: scale(0.95);
}
100% {
opacity: 1;
transform: scale(1);
}
}
</style>
58 changes: 58 additions & 0 deletions src/components/modals/NewsLetterWarningModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<ModalTemplate modalName="NewsLetterWarningModal"
:modal-title="modalTitle">
<div class="c-warning-content">
<p class="c-para-1">Google isn't delivering our newsletter to all subscribers. They aren't even sending it to junk, just black holing it.</p>
<p class="c-para-2">For that reason we can't accept any Gmail addresses. We recommend one of these alternatives:</p>

<ul class="c-url-list">
<li v-for="link in links" :key="link.id">
<a class="is-link" target="_blank" :href="link.url">{{ link.url }}</a>
</li>
</ul>
</div>
</ModalTemplate>
</template>

<script setup lang="ts">
import ModalTemplate from './ModalTemplate.vue'
const modalTitle = 'Sorry Gmail users!'
const links: any[] = [
{ id: 'tuta', url: 'https://tuta.com/' },
{ id: 'startmail', url: 'https://www.startmail.com/' },
{ id: 'proton', url: 'https://proton.me/mail' },
{ id: 'fastmail', url: 'https://www.fastmail.com/' }
]
</script>

<style lang="scss" scoped>
@import "../../styles/_variables";
.c-warning-content {
position: relative;
color: $text_0;
}
.c-para-1 {
margin-bottom: 1.5rem;
}
.c-para-2 {
margin-bottom: 1rem;
}
.c-url-list {
position: relative;
margin-left: 1.25rem;
list-style-type: disc;
li {
margin-bottom: 0.25rem;
a {
font-size: 0.925em;
}
}
}
</style>
2 changes: 1 addition & 1 deletion src/layouts/DefaultLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { SITE_TITLE, SITE_DESCRIPTION } from '../config';
<main>
<slot />
</main>
<Footer client:load />
<Footer client:only="vue" />
</PageWrapper>
</body>
</html>
Loading

0 comments on commit 2496513

Please sign in to comment.