feat(messages): read receipts in group chat, fix group_manage 500 error
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
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
- Show small avatars under each message indicating who has read up to that point - Fix BuildError: group_update → group_edit in group_manage template Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
43b48983c4
commit
afaf88f43f
@ -268,6 +268,26 @@ def group_view(group_id):
|
||||
MessageGroupMember.group_id == group_id
|
||||
).order_by(MessageGroupMember.role, MessageGroupMember.joined_at).all()
|
||||
|
||||
# Build read receipts: for each message, find who has read up to that point
|
||||
# Show avatar at the LAST message they've read (like Teams/WhatsApp)
|
||||
read_receipts = {} # message_id -> list of (user, avatar_path)
|
||||
if messages:
|
||||
other_members = [m for m in members if m.user_id != current_user.id]
|
||||
for m in other_members:
|
||||
if not m.last_read_at:
|
||||
continue
|
||||
# Find the last message this member has read
|
||||
last_read_msg = None
|
||||
for msg in messages:
|
||||
if msg.created_at <= m.last_read_at:
|
||||
last_read_msg = msg
|
||||
else:
|
||||
break
|
||||
if last_read_msg:
|
||||
if last_read_msg.id not in read_receipts:
|
||||
read_receipts[last_read_msg.id] = []
|
||||
read_receipts[last_read_msg.id].append(m.user)
|
||||
|
||||
# Mark as read
|
||||
membership.last_read_at = datetime.now()
|
||||
db.commit()
|
||||
@ -276,7 +296,8 @@ def group_view(group_id):
|
||||
group=group,
|
||||
messages=messages,
|
||||
members=members,
|
||||
membership=membership
|
||||
membership=membership,
|
||||
read_receipts=read_receipts
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@ -350,7 +350,7 @@
|
||||
{% if membership.is_owner %}
|
||||
<div class="manage-card">
|
||||
<h2>Informacje o grupie</h2>
|
||||
<form method="POST" action="{{ url_for('messages.group_update', group_id=group.id) }}">
|
||||
<form method="POST" action="{{ url_for('messages.group_edit', group_id=group.id) }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-group">
|
||||
<label for="group-name">Nazwa grupy</label>
|
||||
|
||||
@ -267,6 +267,42 @@
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
/* Read receipts */
|
||||
.msg-read-receipts {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.read-receipt {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.read-receipt img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.read-receipt span {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Reply form */
|
||||
.reply-section {
|
||||
background: var(--surface);
|
||||
@ -417,6 +453,19 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if read_receipts and read_receipts.get(msg.id) %}
|
||||
<div class="msg-read-receipts">
|
||||
{% for reader in read_receipts[msg.id] %}
|
||||
<div class="read-receipt" title="{{ reader.name or reader.email.split('@')[0] }}">
|
||||
{% if reader.avatar_path %}
|
||||
<img src="{{ url_for('static', filename=reader.avatar_path) }}" alt="">
|
||||
{% else %}
|
||||
<span>{{ (reader.name or reader.email)[0].upper() }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user