feat(messages): delete 1:1 messages and threads from detail view
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

Trash icon in message detail header. Deletes message with all replies
and attachments. Uses nordaConfirm() styled modal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-20 12:37:40 +01:00
parent c18f65e3f3
commit 0812ec0fb3
3 changed files with 58 additions and 1 deletions

View File

@ -260,6 +260,7 @@ def register_blueprints(app):
'messages_send': 'messages.messages_send',
'messages_view': 'messages.messages_view',
'messages_reply': 'messages.messages_reply',
'messages_delete': 'messages.messages_delete',
'api_unread_count': 'messages.api_unread_count',
'api_notifications': 'messages.api_notifications',
'api_notification_mark_read': 'messages.api_notification_mark_read',

View File

@ -427,6 +427,58 @@ def messages_view(message_id):
db.close()
@bp.route('/wiadomosci/<int:message_id>/usun', methods=['POST'])
@login_required
@member_required
def messages_delete(message_id):
"""Usuń wiadomość (nadawca lub odbiorca)"""
db = SessionLocal()
try:
message = db.query(PrivateMessage).options(
joinedload(PrivateMessage.attachments)
).filter(PrivateMessage.id == message_id).first()
if not message:
flash('Wiadomość nie istnieje.', 'error')
return redirect(url_for('.messages_inbox'))
if message.sender_id != current_user.id and message.recipient_id != current_user.id:
flash('Brak dostępu do tej wiadomości.', 'error')
return redirect(url_for('.messages_inbox'))
# Delete attachments from disk
import os
from database import MessageAttachment
for att in message.attachments:
try:
filepath = os.path.join('static', 'uploads', 'messages',
att.created_at.strftime('%Y'), att.created_at.strftime('%m'), att.stored_filename)
if os.path.exists(filepath):
os.remove(filepath)
except Exception:
pass
# Delete replies if this is root message
replies = db.query(PrivateMessage).filter(PrivateMessage.parent_id == message_id).all()
for reply in replies:
for att in reply.attachments:
try:
filepath = os.path.join('static', 'uploads', 'messages',
att.created_at.strftime('%Y'), att.created_at.strftime('%m'), att.stored_filename)
if os.path.exists(filepath):
os.remove(filepath)
except Exception:
pass
db.delete(reply)
db.delete(message)
db.commit()
flash('Wiadomość usunięta.', 'success')
return redirect(url_for('.messages_inbox'))
finally:
db.close()
@bp.route('/api/messages/upload-image', methods=['POST'])
@login_required
@member_required

View File

@ -495,8 +495,12 @@
{% endif %}
<div class="message-card-header">
<div class="message-subject-row">
<div class="message-subject-row" style="display: flex; align-items: center; justify-content: space-between;">
<div class="message-subject">{{ message.subject or '(brak tematu)' }}</div>
<form method="POST" action="{{ url_for('messages.messages_delete', message_id=message.id) }}" style="margin: 0;" onsubmit="return nordaConfirm(this, '{% if thread %}Cały wątek ({{ thread|length }} wiadomości) zostanie trwale usunięty.{% else %}Wiadomość zostanie trwale usunięta.{% endif %}', {title: 'Usunąć {% if thread %}wątek{% else %}wiadomość{% endif %}?', icon: '🗑️', okText: 'Tak, usuń'});">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" title="Usuń {% if thread %}wątek{% else %}wiadomość{% endif %}" style="background: none; border: none; cursor: pointer; color: var(--text-secondary); font-size: 16px; padding: 4px 8px; border-radius: var(--radius); transition: var(--transition);" onmouseover="this.style.color='#dc2626'" onmouseout="this.style.color='var(--text-secondary)'">🗑</button>
</form>
</div>
<div class="message-participants">
<a href="{{ url_for('public.user_profile', user_id=message.sender_id) }}" class="participant-avatar {% if message.sender_id == current_user.id %}is-me{% endif %}" style="text-decoration: none; color: white;">