Files
devplacepy/devplacepy/templates/messages.html
T
retoor 741d7aade6 docs: add block/mute user relations, emoji-sync CLI, and uid indexes
- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications
- Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py`
- Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views
- Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES`
- Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
2026-06-19 08:06:09 +00:00

78 lines
4.7 KiB
HTML

{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="{{ static_url('/static/css/messages.css') }}">
{% endblock %}
{% block footer %}{% endblock %}
{% block content %}
<h1 class="sr-only">Messages</h1>
<div class="page-messages">
<div class="messages-layout" data-self-uid="{{ user['uid'] }}"{% if current_conversation %} data-other-uid="{{ current_conversation }}" data-other-online="{{ '1' if other_online else '0' }}"{% if other_last_seen %} data-other-last-seen="{{ other_last_seen }}"{% endif %}{% endif %}>
<div class="messages-list" role="navigation" aria-label="Conversations">
<div class="messages-list-header" role="search">
<input type="text" id="message-search" placeholder="Search conversations..." value="{{ search }}" aria-label="Search conversations">
</div>
<div class="messages-conversations" role="list" aria-label="Conversation list">
{% for conv in conversations %}
<a href="/messages?with_uid={{ conv.other_user['uid'] }}" class="conversation-item {% if current_conversation == conv.other_user['uid'] %}active{% endif %}" data-conv-uid="{{ conv.other_user['uid'] }}" role="listitem"{% if current_conversation == conv.other_user['uid'] %} aria-current="true"{% endif %}>
<img src="{{ avatar_url('multiavatar', conv.other_user['username'], 32) }}" class="avatar-img avatar-sm" alt="{{ conv.other_user['username'] }}" loading="lazy">
<div class="conversation-info">
<div class="conversation-name">{{ conv.other_user['username'] }}</div>
<div class="conversation-preview">{{ conv.last_message[:60] }}</div>
</div>
<span class="conversation-unread-dot"{% if not conv.unread %} hidden{% endif %}><span class="sr-only">Unread messages</span></span>
<span class="conversation-time">{{ dt_ago(conv.last_message_at) if conv.last_message_at else format_date(conv.last_message_at) }}</span>
</a>
{% else %}
<div class="empty-state" style="border: none;">No conversations yet</div>
{% endfor %}
</div>
</div>
<div class="messages-main">
{% if other_user %}
<div class="messages-main-header">
<button type="button" class="messages-back-btn" id="messages-back-btn" aria-label="Back to conversations">&#x2190;</button>
{% set _user = other_user %}{% set _size = 32 %}{% set _size_class = "sm" %}{% include "_avatar_link.html" %}
<div class="messages-header-info">
<h3>{% set _user = other_user %}{% set _class = none %}{% include "_user_link.html" %}</h3>
<span class="messages-presence" id="messages-presence" role="status" aria-live="polite"></span>
</div>
</div>
<div class="messages-thread" role="log" aria-label="Message transcript" aria-live="polite" aria-relevant="additions">
{% for msg in messages %}
<div class="message-bubble {% if msg.is_mine %}mine{% else %}theirs{% endif %}" data-msg-uid="{{ msg.message['uid'] }}">
<div class="rendered-content">{{ render_content(msg.message['content']) }}</div>
{% if msg.attachments %}
{% with attachments=msg.attachments %}
{% include "_attachment_display.html" %}
{% endwith %}
{% endif %}
<span class="message-time">{{ dt_ago(msg.created_at) if msg.created_at else msg.time_ago }}</span>
{% if msg.is_mine %}<span class="message-receipt"{% if not msg.message['read'] %} hidden{% endif %}>&#x2713;&#x2713;<span class="sr-only">Read</span></span>{% endif %}
</div>
{% endfor %}
<div class="typing-indicator" id="typing-indicator" hidden><span></span><span></span><span></span></div>
</div>
<form class="messages-input-area" method="POST" action="/messages/send" data-live-form>
<input type="hidden" name="receiver_uid" value="{{ other_user['uid'] }}">
<input type="text" name="content" placeholder="Type a message..." maxlength="2000" autocomplete="off" data-mention aria-label="Type a message">
<dp-upload multiple
max-size="{{ max_upload_size_mb() }}"
max-files="5"
allowed-types="{{ allowed_file_types() }}"></dp-upload>
<button type="submit" class="messages-send-btn" aria-label="Send"><span class="send-icon">&#x27A4;</span><span class="send-spinner" aria-hidden="true"></span></button>
</form>
{% else %}
<div class="messages-empty">
<p>Select a conversation to start chatting</p>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}