feat(forum): email + visual highlight for @mentions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

- parse_mentions_and_notify now sends email to mentioned user
  (separate from forum subscription emails — fires on every mention)
- parse_forum_markdown accepts current_user_name; mentions matching
  the viewer get extra .forum-mention-self class
- topic.html passes current_user.name to filter; .forum-mention-self
  styled with amber background + bold + ring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-04-13 13:47:30 +02:00
parent dc6c711264
commit 836594079f
3 changed files with 68 additions and 10 deletions

View File

@ -200,6 +200,13 @@
font-weight: 500;
}
.forum-mention-self {
background: #fde68a;
color: #78350f;
font-weight: 700;
box-shadow: 0 0 0 1px #f59e0b;
}
/* User stats tooltip */
.user-stats-trigger {
cursor: pointer;
@ -1105,7 +1112,7 @@
</button>
</div>
<div class="topic-content" id="topicContent">{{ topic.content|forum_markdown }}</div>
<div class="topic-content" id="topicContent">{{ topic.content|forum_markdown(current_user.name if current_user.is_authenticated else None) }}</div>
<!-- Reactions bar for topic -->
<div class="reactions-bar" id="topicReactions" data-content-type="topic" data-content-id="{{ topic.id }}">
@ -1235,7 +1242,7 @@
{% if reply.is_deleted %}
<div class="reply-content deleted-notice">[Ta odpowiedź została usunięta]</div>
{% else %}
<div class="reply-content">{{ reply.content|forum_markdown }}</div>
<div class="reply-content">{{ reply.content|forum_markdown(current_user.name if current_user.is_authenticated else None) }}</div>
{% if reply.attachments %}
<div class="reply-attachments-container">

View File

@ -19,7 +19,7 @@ def _autolink(text):
)
def parse_forum_markdown(text):
def parse_forum_markdown(text, current_user_name=None):
"""
Convert markdown text to safe HTML.
@ -81,12 +81,18 @@ def parse_forum_markdown(text):
text
)
# @mentions - highlight them
text = re.sub(
r'@([\w.\-]+)',
r'<span class="forum-mention">@\1</span>',
text
)
# @mentions - highlight them; mark self-mentions with extra class
self_variants = set()
if current_user_name:
norm = current_user_name.strip().lower()
self_variants = {norm.replace(' ', '.'), norm.replace(' ', '_'), norm.replace(' ', '')}
def _render_mention(m):
handle = m.group(1).lower()
cls = 'forum-mention forum-mention-self' if handle in self_variants else 'forum-mention'
return f'<span class="{cls}">@{m.group(1)}</span>'
text = re.sub(r'@([\w.\-]+)', _render_mention, text)
# Now process block structure (lists, quotes, paragraphs)
lines = text.split('\n')

View File

@ -621,6 +621,7 @@ def parse_mentions_and_notify(content, author_id, author_name, topic_id, content
if user:
mentioned_user_ids.append(user.id)
action_url = f'/forum/{topic_id}{"#reply-" + str(content_id) if content_type == "reply" else ""}'
create_notification(
user_id=user.id,
title=f"@{author_name} wspomniał o Tobie",
@ -628,9 +629,53 @@ def parse_mentions_and_notify(content, author_id, author_name, topic_id, content
notification_type='message',
related_type=f'forum_{content_type}',
related_id=content_id,
action_url=f'/forum/{topic_id}{"#reply-" + str(content_id) if content_type == "reply" else ""}'
action_url=action_url
)
# Send email notification to mentioned user
try:
from email_service import send_email, _email_v3_wrap
base_url = "https://nordabiznes.pl"
full_url = base_url + action_url
preview = (content[:200] + '...') if len(content) > 200 else content
where = 'odpowiedzi' if content_type == 'reply' else 'temacie'
subject = f"{author_name} wspomniał o Tobie na forum"
body_text = f"""{author_name} wspomniał o Tobie w {where} na forum Norda Biznes.
"{preview}"
Zobacz: {full_url}
---
Norda Biznes Partner - https://nordabiznes.pl
"""
html_content = f'''
<p style="margin:0 0 16px; color:#1e293b; font-size:16px;">Cześć <strong>{user.name or ''}</strong>!</p>
<p style="margin:0 0 24px; color:#475569; font-size:15px; line-height:1.5;"><strong>{author_name}</strong> wspomniał o Tobie w {where} na forum:</p>
<table width="100%" cellpadding="0" cellspacing="0" style="background:#fef3c7; border-left: 4px solid #f59e0b; border-radius: 0 8px 8px 0; margin-bottom:28px;">
<tr><td style="padding: 16px 20px;">
<p style="margin:0; color:#475569; font-size:14px; font-style:italic; line-height:1.6;">{preview}</p>
</td></tr>
</table>
<table width="100%" cellpadding="0" cellspacing="0" style="margin-bottom:20px;">
<tr><td align="center" style="padding: 8px 0;">
<a href="{full_url}" style="display:inline-block; padding:16px 40px; background: linear-gradient(135deg, #1e3a8a, #172554); color:#ffffff; text-decoration:none; border-radius:8px; font-size:15px; font-weight:600;">Zobacz na forum &rarr;</a>
</td></tr>
</table>'''
body_html = _email_v3_wrap('Wspomniano o Tobie', 'Norda Biznes Partner', html_content)
send_email(
to=[user.email],
subject=subject,
body_text=body_text,
body_html=body_html,
email_type='forum_mention',
recipient_name=user.name or ''
)
except Exception as e:
logger.error(f"Failed to send mention email to {user.email}: {e}")
return mentioned_user_ids
except Exception as e: