@@ -2243,7 +2243,9 @@ _setThreadParentHeader(meta = {}) {
22432243} ,
22442244
22452245_setThreadReply ( msgEl , msgId ) {
2246- const author = msgEl . querySelector ( '.thread-msg-author' ) ?. textContent || msgEl . dataset . username || 'someone' ;
2246+ const author = msgEl . querySelector ( '.thread-msg-author' ) ?. textContent
2247+ || this . _getNickname ?. ( parseInt ( msgEl . dataset . userId , 10 ) , msgEl . dataset . username )
2248+ || msgEl . dataset . username || 'someone' ;
22472249 const rawContent = msgEl . dataset . rawContent || msgEl . querySelector ( '.thread-msg-content' ) ?. textContent || '' ;
22482250 const preview = rawContent . length > 70 ? rawContent . substring ( 0 , 70 ) + '…' : rawContent ;
22492251 this . _threadReplyingTo = { id : msgId , username : author , content : rawContent } ;
@@ -2266,7 +2268,9 @@ _clearThreadReply() {
22662268
22672269_quoteThreadMessage ( msgEl ) {
22682270 const rawContent = msgEl . dataset . rawContent || msgEl . querySelector ( '.thread-msg-content' ) ?. textContent || '' ;
2269- const author = msgEl . querySelector ( '.thread-msg-author' ) ?. textContent || msgEl . dataset . username || 'someone' ;
2271+ const author = msgEl . querySelector ( '.thread-msg-author' ) ?. textContent
2272+ || this . _getNickname ?. ( parseInt ( msgEl . dataset . userId , 10 ) , msgEl . dataset . username )
2273+ || msgEl . dataset . username || 'someone' ;
22702274 const quotedLines = rawContent . split ( '\n' ) . map ( l => `> ${ l } ` ) . join ( '\n' ) ;
22712275 const quoteText = `> @${ author } wrote:\n${ quotedLines } \n` ;
22722276
@@ -2947,9 +2951,12 @@ _appendThreadMessage(msg) {
29472951 el . dataset . rawContent = msg . content ;
29482952 el . dataset . userId = msg . user_id ;
29492953 el . dataset . time = msg . created_at ;
2950- // Stash the display name so reply/quote still resolve the author on compact
2951- // rows, which don't render a `.thread-msg-author` element.
2952- el . dataset . username = displayName ;
2954+ // Stash the raw username + avatar so a compact row can be promoted back to a
2955+ // full row (with the header restored) if the group head above it is deleted,
2956+ // and so reply/quote can resolve the author on compact rows that have no
2957+ // `.thread-msg-author` element.
2958+ el . dataset . username = msg . username || '' ;
2959+ if ( msg . avatar ) el . dataset . avatar = msg . avatar ;
29532960 if ( msg . persona_id ) el . dataset . personaId = String ( msg . persona_id ) ;
29542961 if ( threadCompact ) {
29552962 const shortTime = new Date ( msg . created_at ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) ;
@@ -2994,6 +3001,50 @@ _appendThreadMessage(msg) {
29943001 container . scrollTop = container . scrollHeight ;
29953002} ,
29963003
3004+ // Promote a compact thread reply back to a full row (avatar + author header
3005+ // restored), keeping its existing content/toolbar/reactions. Called when the
3006+ // group head above it is deleted, so the new head still shows who sent it —
3007+ // the thread mirror of `_promoteCompactToFull`.
3008+ _promoteThreadCompactToFull ( compactEl ) {
3009+ if ( ! compactEl ) return ;
3010+ const userId = parseInt ( compactEl . dataset . userId , 10 ) ;
3011+ const rawUsername = compactEl . dataset . username || t ( 'app.messages.unknown_user' ) ;
3012+ const displayName = this . _getNickname ?. ( userId , rawUsername ) || rawUsername ;
3013+ const time = compactEl . dataset . time ;
3014+ const color = this . _getUserColor ( rawUsername ) ;
3015+ const initial = ( displayName || '?' ) . charAt ( 0 ) . toUpperCase ( ) ;
3016+
3017+ // Preserve the already-rendered content, toolbar, and reactions.
3018+ const contentHtml = compactEl . querySelector ( '.thread-msg-content' ) ?. innerHTML || '' ;
3019+ const toolbarHtml = compactEl . querySelector ( '.thread-msg-toolbar' ) ?. outerHTML || '' ;
3020+ const reactionsHtml = compactEl . querySelector ( '.reactions-row' ) ?. outerHTML || '' ;
3021+
3022+ // Avatar: stored at render time, else the online/member list, else initial.
3023+ const _pool = ( this . _lastOnlineUsers || [ ] ) . concat ( this . channelMembers || [ ] ) ;
3024+ const onlineUser = _pool . find ( u => u . id === userId ) || null ;
3025+ const avatar = compactEl . dataset . avatar || ( onlineUser && onlineUser . avatar ) || null ;
3026+ const avatarHtml = avatar
3027+ ? `<img class="thread-msg-avatar" src="${ this . _escapeHtml ( avatar ) } " alt="${ initial } ">`
3028+ : `<div class="thread-msg-avatar thread-msg-avatar-initial" style="background:${ color } ">${ initial } </div>` ;
3029+
3030+ compactEl . classList . remove ( 'thread-compact' ) ;
3031+ compactEl . innerHTML = `
3032+ <div class="thread-msg-row">
3033+ ${ avatarHtml }
3034+ <div class="thread-msg-body">
3035+ <div class="thread-msg-header">
3036+ <span class="thread-msg-author" style="color:${ color } ">${ this . _escapeHtml ( displayName ) } </span>
3037+ <span class="thread-msg-time">${ this . _formatTime ( time ) } </span>
3038+ <span class="thread-msg-header-spacer"></span>
3039+ ${ toolbarHtml }
3040+ </div>
3041+ <div class="thread-msg-content">${ contentHtml } </div>
3042+ ${ reactionsHtml }
3043+ </div>
3044+ </div>
3045+ ` ;
3046+ } ,
3047+
29973048_updateThreadPreview ( parentId , thread ) {
29983049 const msgEl = document . querySelector ( `[data-msg-id="${ parentId } "]` ) ;
29993050 if ( ! msgEl ) return ;
0 commit comments