mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 08:56:52 +00:00
Feature/saajan persona (#3)
* feat: add template based email content (html + plaintext fallback) * feat: init saajan component * feat: add aesthetic noise background and implement Saajan component in register and login * feat: add post seal modal for vault * refactor: add proper props interfaces * refactor: expose props on ui components * feat: add ssajan in lots of flows * fix: remove render test with no value and add aria helper for btn identification * refactor: update email notification to account for proper arguments * refactor: refactor E2E auth helper and mail parsing logic --------- Co-authored-by: ramvignesh-b <ramvignesh-b@github.com>
This commit is contained in:
@@ -84,6 +84,21 @@ MIDDLEWARE = [
|
||||
"django_structlog.middlewares.RequestMiddleware",
|
||||
]
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(BASE_DIR, "templates")],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "config.urls"
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ from datetime import UTC, datetime
|
||||
import structlog
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from django.core.mail import send_mail
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from config import settings
|
||||
from config.settings import FRONTEND_URLS
|
||||
from letters.models import Letter
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -23,9 +25,26 @@ def notify_unlocked_letter(letter):
|
||||
"""
|
||||
author = letter.user.get_username()
|
||||
try:
|
||||
send_mail(subject="", message="", from_email=settings.FROM_EMAIL, recipient_list=[author], fail_silently=False)
|
||||
letter_link = f"{FRONTEND_URLS[0]}/read/{letter.public_id}"
|
||||
subject = "A letter. Written for this exact moment."
|
||||
context = {
|
||||
"pen_name": letter.user.first_name,
|
||||
"cta": {"title": "View what you wrote", "link": letter_link},
|
||||
"footnote": True,
|
||||
}
|
||||
plaint_content = render_to_string("email/vault_unlock.txt", context=context)
|
||||
html_content = render_to_string("email/vault_unlock.html", context=context)
|
||||
send_mail(
|
||||
subject=subject,
|
||||
message=plaint_content,
|
||||
from_email=settings.FROM_EMAIL,
|
||||
recipient_list=[author],
|
||||
fail_silently=False,
|
||||
html_message=html_content,
|
||||
)
|
||||
letter.notified_at = datetime.now(UTC)
|
||||
letter.save()
|
||||
logger.info(f"Successfully notified {author} of unlocked letter")
|
||||
except Exception:
|
||||
logger.exception(f"Failed to notify {author} of unlocked letter")
|
||||
|
||||
|
||||
@@ -396,6 +396,7 @@ class LetterTaskTest(TestCase):
|
||||
from_email=settings.FROM_EMAIL,
|
||||
recipient_list=[self.user.email],
|
||||
fail_silently=False,
|
||||
html_message=ANY,
|
||||
)
|
||||
self.assertIsNotNone(letter_to_notify1.notified_at)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{% extends 'email/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div style="padding: 15px; font-style: italic">
|
||||
<p>{{ pen_name }},</p>
|
||||
<p>
|
||||
Your destination is one train away.
|
||||
</p>
|
||||
<p>I've been keeping a place for your words.<br/>
|
||||
Come when you're ready.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footnote %}
|
||||
This link expires in 24 hours.<br/>
|
||||
I'm patient, but not endlessly so.
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
Didn't write to me? Then someone else did.<br/>
|
||||
Ignore this. I'll forget you were ever here.
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,21 @@
|
||||
pi. ku.
|
||||
-------------------------------------------
|
||||
|
||||
{{pen_name}},
|
||||
|
||||
Your destination is one train away.
|
||||
|
||||
I've been keeping a place for your words.
|
||||
Come when you're ready.
|
||||
|
||||
{{ cta.title }} -> {{ cta.link }}
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
This link expires in 24 hours.
|
||||
I'm patient, but not endlessly so.
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
Didn't write to me? Then someone else did.
|
||||
Ignore this. I'll forget you were ever here.
|
||||
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>pi. ku.</title>
|
||||
</head>
|
||||
<body style="margin:0; padding:0; background-color:#1a1712;">
|
||||
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"
|
||||
style="background-color:#1a1712;">
|
||||
<tr>
|
||||
<td align="center" style="padding: 48px 16px;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"
|
||||
style="max-width:480px; width:100%;">
|
||||
|
||||
{# Logo #}
|
||||
<tr>
|
||||
<td align="left" style="padding-bottom: 36px;">
|
||||
<img src="https://cdn.jsdelivr.net/gh/ramvignesh-b/cdn@main/pi-ku_logo.png" width="100"
|
||||
height="50" alt="Pi.Ku"
|
||||
style="display:block;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{# Body #}
|
||||
<tr>
|
||||
<td style="font-family: Lora, Georgia, serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.9;
|
||||
color: #cdccca;
|
||||
font-style: italic;
|
||||
padding-bottom: 32px;">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{# CTA #}
|
||||
{% if cta %}
|
||||
<tr>
|
||||
<td align="left" style="padding-bottom: 32px;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td style="background-color: #301e19; border-radius: 3px;">
|
||||
<a href='{{ cta.link }}'
|
||||
style="display: inline-block;
|
||||
padding: 12px 28px;
|
||||
font-family: Georgia, serif;
|
||||
font-size: 14px;
|
||||
color: #f5e6c8;
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.04em;">
|
||||
{{ cta.title }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if footnote %}
|
||||
<tr>
|
||||
<td style="font-family: Lora, Georgia, serif;
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
color: #7a7974;
|
||||
padding-bottom: 40px;
|
||||
line-height: 1.8;">
|
||||
{% block footnote %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{# Footer #}
|
||||
<tr>
|
||||
<td style="border-top: 1px solid #2e2c29; padding-bottom: 24px; font-size: 0;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: Lora, serif;
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
color: #5a5957;
|
||||
line-height: 1.8;">
|
||||
{% block footer %}
|
||||
{% endblock %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends 'email/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
Time has a way of making things clearer.<br/>
|
||||
Or heavier. Sometimes both.
|
||||
</p>
|
||||
<p>
|
||||
You had something to say at this exact moment.<br/>
|
||||
I kept it exactly as you left it. <br/>
|
||||
Not a word changed. Not a word read.
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block footnote %}
|
||||
<p>
|
||||
You're ready now. Or maybe you're still not.<br/>
|
||||
Open it anyway. You won't regret it.
|
||||
</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,17 @@
|
||||
pi. ku.
|
||||
-------------------------------------------
|
||||
|
||||
{{pen_name}},
|
||||
|
||||
Time has a way of making things clearer.
|
||||
Or heavier. Sometimes both.
|
||||
|
||||
You had something to say at this exact moment.
|
||||
I kept it exactly as you left it.
|
||||
Not a word changed. Not a word read.
|
||||
|
||||
{{ cta.title }} -> {{ cta.link }}
|
||||
|
||||
-------------------------------------------
|
||||
You're ready now. Or maybe you're still not.
|
||||
Open it anyway. You won't regret it.
|
||||
+20
-10
@@ -1,6 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core.mail import send_mail
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
|
||||
@@ -9,16 +10,25 @@ def send_activation_email(user):
|
||||
token = default_token_generator.make_token(user)
|
||||
uid = urlsafe_base64_encode(force_bytes(user.public_id))
|
||||
activation_url = f"{settings.FRONTEND_URLS[0]}/activate/{uid}/{token}"
|
||||
subject = "Activate Your Piku Account"
|
||||
message = f"""Hi {user.full_name},
|
||||
|
||||
Welcome to Pi Ku.
|
||||
|
||||
Please click the link below to activate your account:
|
||||
>> {activation_url}
|
||||
|
||||
If you did not create this account, please ignore this email."""
|
||||
send_mail(subject, message, settings.FROM_EMAIL, [user.email], fail_silently=False)
|
||||
subject = "Activate your pi. ku. account"
|
||||
context = {
|
||||
"pen_name": user.full_name,
|
||||
"footnote": True,
|
||||
"cta": {
|
||||
"title": "Onboard",
|
||||
"link": activation_url,
|
||||
},
|
||||
}
|
||||
html_content = render_to_string("email/activation.html", context)
|
||||
plain_content = render_to_string("email/activation.txt", context)
|
||||
send_mail(
|
||||
subject=subject,
|
||||
message=plain_content,
|
||||
from_email=settings.FROM_EMAIL,
|
||||
recipient_list=[user.email],
|
||||
fail_silently=False,
|
||||
html_message=html_content,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user