Files
BountyBoard/web/templates/messages.html
T
etalon 7d3140334e feat: messaging with conversations, rich text, attachments, typing, unread (phase 9)
- conversations: dm (unique pair, find-or-create), group (titled), project
  (customer-bound); participant-only access
- messages: server-sanitized rich text, attachments referencing uploads
  from the generic POST /api/v1/files endpoint (rate limited, MIME sniffed)
- unread counts via aggregation; mark-read pushes readBy receipts
- chat files served only to conversation participants (or uploader)
- @mentions in chat notify the mentioned participant
- typing indicator: client WS event relayed to other participants
- WS targeted delivery (SendTo) + 15s polling fallback
- two-pane messages UI: conversation list with unread badges, composer
  (contenteditable + formatting buttons), drag & drop upload, inline image
  previews with lightbox, new-conversation dialog with user search

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:15:56 +02:00

73 lines
3.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
<div class="error-box" id="error" role="alert"></div>
<div class="chat-layout">
<aside class="chat-sidebar card">
<div class="spread">
<h2>Messages</h2>
<button class="btn small primary" id="conv-new"> New</button>
</div>
<ul id="conv-list" class="conv-list"></ul>
</aside>
<section class="chat-main card">
<div class="spread">
<h2 id="chat-title">Select a conversation</h2>
<span class="muted" id="typing-indicator" aria-live="polite"></span>
</div>
<div id="chat-messages" class="chat-messages" aria-live="polite"></div>
<form id="chat-form" hidden>
<div id="chat-attachments" class="chat-attachments"></div>
<div class="chat-toolbar" role="toolbar" aria-label="Formatting">
<button class="btn small" type="button" data-cmd="bold" aria-label="Bold"><b>B</b></button>
<button class="btn small" type="button" data-cmd="italic" aria-label="Italic"><i>I</i></button>
<button class="btn small" type="button" data-cmd="underline" aria-label="Underline"><u>U</u></button>
<button class="btn small" type="button" data-cmd="strikeThrough" aria-label="Strikethrough"><s>S</s></button>
<button class="btn small" type="button" data-cmd="insertUnorderedList" aria-label="Bulleted list">• list</button>
<label class="btn small" style="cursor:pointer">📎<input type="file" id="chat-file" multiple hidden></label>
</div>
<div id="chat-composer" class="chat-composer" contenteditable="true" role="textbox"
aria-multiline="true" aria-label="Message" data-placeholder="Write a message… (drag & drop files)"></div>
<div class="spread mt">
<span class="hint">Enter to send · Shift+Enter for newline</span>
<button class="btn primary" type="submit">Send</button>
</div>
</form>
</section>
</div>
<dialog id="newconv-dialog">
<form method="dialog" id="newconv-form" class="stack" style="min-width:420px">
<h2>New conversation</h2>
<div class="field">
<label for="nc-kind">Kind</label>
<select id="nc-kind">
<option value="dm">Direct message</option>
<option value="group">Group</option>
<option value="project">Project</option>
</select>
</div>
<div class="field" id="nc-title-wrap" hidden>
<label for="nc-title">Title</label>
<input type="text" id="nc-title">
</div>
<div class="field" id="nc-customer-wrap" hidden>
<label for="nc-customer">Customer id</label>
<input type="text" id="nc-customer" placeholder="paste customer id">
</div>
<div class="field">
<label for="nc-search">Participants</label>
<input type="search" id="nc-search" placeholder="Search people…">
<div id="nc-results" class="stack" style="max-height:160px;overflow:auto"></div>
<div id="nc-selected" class="mt"></div>
</div>
<div class="spread">
<button class="btn" type="button" id="nc-cancel">Cancel</button>
<button class="btn primary" type="submit">Start conversation</button>
</div>
</form>
</dialog>
<dialog id="lightbox" class="lightbox">
<img id="lightbox-img" alt="">
</dialog>
{{end}}