settings: digest opt-in + tone (PATCH /api/settings/digest + UI)
Adds DigestPrefsIn/Out models, PATCH /api/settings/digest endpoint, email digest section in settings.html, and last_email_send context in pages.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5c89f4d04a
commit
14fe47103f
4 changed files with 176 additions and 1 deletions
|
|
@ -96,6 +96,71 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{# --- Email digests block ------------------------------------------ #}
|
||||
<div class="settings-section">
|
||||
<div class="settings-section__head">Email digests</div>
|
||||
<p class="settings-section__lede">
|
||||
Editorial commentary delivered to your inbox. Daily for paid (Mon–Sat) plus the Sunday recap; free tier gets the Sunday recap.
|
||||
</p>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row__label">Subscription</div>
|
||||
<div class="settings-row__value">
|
||||
<label style="display:block; margin-bottom:8px;">
|
||||
<input type="checkbox" id="digest-opt-in"
|
||||
{% if user.email_digest_opt_in %}checked{% endif %}>
|
||||
Send me digests
|
||||
</label>
|
||||
<div class="settings-row__hint" style="margin-bottom:8px;">
|
||||
One-click unsubscribe in every email.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row__label">Reading level</div>
|
||||
<div class="settings-row__value">
|
||||
<div style="display:flex; gap:14px;">
|
||||
<label><input type="radio" name="digest-tone" value="NOVICE"
|
||||
{% if (user.digest_tone or 'INTERMEDIATE') == 'NOVICE' %}checked{% endif %}> Novice</label>
|
||||
<label><input type="radio" name="digest-tone" value="INTERMEDIATE"
|
||||
{% if (user.digest_tone or 'INTERMEDIATE') == 'INTERMEDIATE' %}checked{% endif %}> Intermediate</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row__label">Last delivery</div>
|
||||
<div class="settings-row__value settings-row__hint">
|
||||
<span id="digest-last">{% if last_email_send %}{{ last_email_send.sent_at.strftime("%Y-%m-%d %H:%M") }} UTC — {{ last_email_send.status }}{% else %}—{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="digest-feedback" class="settings-row__hint" style="margin-top:6px;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const opt = document.getElementById('digest-opt-in');
|
||||
const tones = document.querySelectorAll('input[name="digest-tone"]');
|
||||
const fb = document.getElementById('digest-feedback');
|
||||
if (!opt || !fb) return;
|
||||
function patch() {
|
||||
fb.textContent = 'Saving…';
|
||||
const tone = Array.from(tones).find(t => t.checked)?.value || 'INTERMEDIATE';
|
||||
fetch('/api/settings/digest', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ opt_in: opt.checked, tone: tone }),
|
||||
}).then(r => {
|
||||
fb.textContent = r.ok ? 'Saved.' : 'Could not save — try again.';
|
||||
}).catch(() => { fb.textContent = 'Network error.'; });
|
||||
}
|
||||
opt.addEventListener('change', patch);
|
||||
tones.forEach(t => t.addEventListener('change', patch));
|
||||
})();
|
||||
</script>
|
||||
|
||||
{# --- Cloud sync block --------------------------------------------- #}
|
||||
<div class="settings-section">
|
||||
<div class="settings-section__head">Cloud sync (encrypted)</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue