Mar 02 2026: fix notification update, and sent email

This commit is contained in:
2026-03-02 13:35:28 -05:00
parent 68b180c2e4
commit f73a641aef
3 changed files with 62 additions and 13 deletions
+1 -1
View File
@@ -239,7 +239,7 @@
} }
function escapeAttr(str) { return escapeHtml(str); } function escapeAttr(str) { return escapeHtml(str); }
function fetchNotifications() { window.fetchNotifications = function fetchNotifications() {
fetch(FEED_URL, { credentials: 'same-origin' }) fetch(FEED_URL, { credentials: 'same-origin' })
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
+29 -1
View File
@@ -74,7 +74,7 @@
{% if comments %} {% if comments %}
<div class="card shadow-sm mb-4"> <div class="card shadow-sm mb-4">
<div class="card-header bg-light"> <div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-clock-history"></i> Update History</h6> <h6 class="mb-0" id="update-history"><i class="bi bi-clock-history"></i> Update History</h6>
</div> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
{% for c in comments %} {% for c in comments %}
@@ -181,4 +181,32 @@
<a href="{{ url_for('issues.index') }}" class="btn btn-outline-secondary btn-sm"> <a href="{{ url_for('issues.index') }}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> Back to Issues <i class="bi bi-arrow-left"></i> Back to Issues
</a> </a>
{% block extra_js %}
<script>
(function () {
'use strict';
// ── Immediate bell refresh after a successful update ──────────────────
// After form submit + redirect, Flask flashes 'Issue updated.' and the
// URL stays at /issues/<id>. We detect the flash message presence and
// call the global fetchNotifications() defined in base.html so the bell
// badge updates instantly without waiting for the 60-second poll cycle.
if (document.querySelector('.alert-success')) {
if (typeof fetchNotifications === 'function') {
fetchNotifications();
}
}
// ── Auto-scroll to Update History after save ──────────────────────────
// If there's a success flash AND comments exist, scroll the history
// section into view so the newly added comment is immediately visible.
if (document.querySelector('.alert-success')) {
var historyEl = document.getElementById('update-history');
if (historyEl) {
historyEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
})();
</script>
{% endblock %}
{% endblock %} {% endblock %}
+32 -11
View File
@@ -24,7 +24,7 @@ Digest emails are sent by calling send_pending_digests(frequency) from the
/notifications/send-digest route, which is triggered by a server cron job. /notifications/send-digest route, which is triggered by a server cron job.
""" """
import logging import logging, threading
from flask import current_app, render_template_string from flask import current_app, render_template_string
from flask_mail import Message from flask_mail import Message
from app import db, mail from app import db, mail
@@ -218,7 +218,13 @@ def notify(
def _send_single_email(recipient, title, body, link): def _send_single_email(recipient, title, body, link):
"""Dispatch a single immediate notification email. Fire-and-forget.""" """Dispatch a single immediate notification email in a background thread.
Sending is offloaded to a daemon thread so SMTP latency never blocks the
HTTP response. The Flask application context is pushed explicitly so that
Flask-Mail and config lookups work outside the request context.
"""
# Render templates while still inside the request context
try: try:
base_url = current_app.config.get('APP_BASE_URL', '').rstrip('/') base_url = current_app.config.get('APP_BASE_URL', '').rstrip('/')
sender = current_app.config.get( sender = current_app.config.get(
@@ -238,16 +244,31 @@ def _send_single_email(recipient, title, body, link):
body = text_body, body = text_body,
html = html_body, html = html_body,
) )
mail.send(msg)
logger.info(
'NOTIFICATION EMAIL SENT | to=%s | subject=%s',
recipient.email, msg.subject,
)
except Exception as exc: except Exception as exc:
logger.error( logger.error('NOTIFICATION EMAIL BUILD FAILED | to=%s | error=%s', recipient.email, exc)
'NOTIFICATION EMAIL FAILED | to=%s | error=%s', return
recipient.email, exc,
) # Capture app instance before leaving the request context
app = current_app._get_current_object()
recipient_email = recipient.email
subject = msg.subject
def _send():
with app.app_context():
try:
mail.send(msg)
logger.info(
'NOTIFICATION EMAIL SENT | to=%s | subject=%s',
recipient_email, subject,
)
except Exception as exc:
logger.error(
'NOTIFICATION EMAIL FAILED | to=%s | error=%s',
recipient_email, exc,
)
t = threading.Thread(target=_send, daemon=True)
t.start()
# ── Digest delivery ──────────────────────────────────────────────────────────── # ── Digest delivery ────────────────────────────────────────────────────────────