@@ -1031,18 +1031,30 @@ async function fetchProfiles() {
10311031 await invoke ( "sync_all_profiles" ) ;
10321032}
10331033
1034+ // Track pending status hide timeout
1035+ let statusHideTimeout = null ;
1036+
10341037/**
10351038 * Update the chat header subtext (status/typing indicator) for the currently open chat
10361039 * @param {Object } chat - The chat object
10371040 */
10381041function updateChatHeaderSubtext ( chat ) {
10391042 if ( ! chat ) return ;
10401043
1044+ // Clear any pending hide timeout
1045+ if ( statusHideTimeout ) {
1046+ clearTimeout ( statusHideTimeout ) ;
1047+ statusHideTimeout = null ;
1048+ }
1049+
1050+ let newStatusText = '' ;
1051+ let shouldAddGradient = false ;
1052+
10411053 const isGroup = chat . chat_type === 'MlsGroup' ;
10421054 const fNotes = chat . id === strPubkey ;
10431055 if ( fNotes ) {
1044- domChatContactStatus . textContent = 'Encrypted Notes to Self' ;
1045- domChatContactStatus . classList . remove ( 'text-gradient' ) ;
1056+ newStatusText = 'Encrypted Notes to Self' ;
1057+ shouldAddGradient = false ;
10461058 } else if ( isGroup ) {
10471059 // Check for typing indicators in groups
10481060 const activeTypers = chat . active_typers || [ ] ;
@@ -1053,36 +1065,36 @@ function updateChatHeaderSubtext(chat) {
10531065 if ( activeTypers . length === 1 ) {
10541066 const typer = getProfile ( activeTypers [ 0 ] ) ;
10551067 const name = typer ?. nickname || typer ?. name || 'Someone' ;
1056- domChatContactStatus . textContent = `${ name } is typing...` ;
1068+ newStatusText = `${ name } is typing...` ;
10571069 } else if ( activeTypers . length === 2 ) {
10581070 const typer1 = getProfile ( activeTypers [ 0 ] ) ;
10591071 const typer2 = getProfile ( activeTypers [ 1 ] ) ;
10601072 const name1 = typer1 ?. nickname || typer1 ?. name || 'Someone' ;
10611073 const name2 = typer2 ?. nickname || typer2 ?. name || 'Someone' ;
1062- domChatContactStatus . textContent = `${ name1 } and ${ name2 } are typing...` ;
1074+ newStatusText = `${ name1 } and ${ name2 } are typing...` ;
10631075 } else if ( activeTypers . length === 3 ) {
10641076 const typer1 = getProfile ( activeTypers [ 0 ] ) ;
10651077 const typer2 = getProfile ( activeTypers [ 1 ] ) ;
10661078 const typer3 = getProfile ( activeTypers [ 2 ] ) ;
10671079 const name1 = typer1 ?. nickname || typer1 ?. name || 'Someone' ;
10681080 const name2 = typer2 ?. nickname || typer2 ?. name || 'Someone' ;
10691081 const name3 = typer3 ?. nickname || typer3 ?. name || 'Someone' ;
1070- domChatContactStatus . textContent = `${ name1 } , ${ name2 } , and ${ name3 } are typing...` ;
1082+ newStatusText = `${ name1 } , ${ name2 } , and ${ name3 } are typing...` ;
10711083 } else {
10721084 // 4+ people typing
1073- domChatContactStatus . textContent = `Several people are typing...` ;
1085+ newStatusText = `Several people are typing...` ;
10741086 }
1075- domChatContactStatus . classList . add ( 'text-gradient' ) ;
1087+ shouldAddGradient = true ;
10761088 } else {
10771089 const memberCount = chat . metadata ?. custom_fields ?. member_count ? parseInt ( chat . metadata . custom_fields . member_count ) : null ;
10781090 if ( typeof memberCount === 'number' ) {
10791091 const label = memberCount === 1 ? 'member' : 'members' ;
1080- domChatContactStatus . textContent = `${ memberCount } ${ label } ` ;
1092+ newStatusText = `${ memberCount } ${ label } ` ;
10811093 } else {
10821094 // Avoid misleading "0 members" before first count refresh
1083- domChatContactStatus . textContent = 'Members syncing...' ;
1095+ newStatusText = 'Members syncing...' ;
10841096 }
1085- domChatContactStatus . classList . remove ( 'text-gradient' ) ;
1097+ shouldAddGradient = false ;
10861098 }
10871099 } else {
10881100 // Check for typing indicators in DMs (using chat.active_typers)
@@ -1093,20 +1105,42 @@ function updateChatHeaderSubtext(chat) {
10931105 // For DMs, there should only be one typer (the other person)
10941106 const typer = getProfile ( activeTypers [ 0 ] ) ;
10951107 const name = typer ?. nickname || typer ?. name || 'User' ;
1096- domChatContactStatus . textContent = `${ name } is typing...` ;
1097- domChatContactStatus . classList . add ( 'text-gradient' ) ;
1108+ newStatusText = `${ name } is typing...` ;
1109+ shouldAddGradient = true ;
10981110 } else {
10991111 const profile = getProfile ( chat . id ) ;
1100- domChatContactStatus . textContent = profile ?. status ?. title || '' ;
1101- domChatContactStatus . classList . remove ( 'text-gradient' ) ;
1102- twemojify ( domChatContactStatus ) ;
1112+ newStatusText = profile ?. status ?. title || '' ;
1113+ shouldAddGradient = false ;
11031114 }
11041115 }
11051116
1106- // Update visibility based on whether there's content to show
1107- domChatContactStatus . style . display = ! domChatContactStatus . textContent ? 'none' : '' ;
1108- domChatContact . classList . toggle ( 'chat-contact' , ! domChatContactStatus . textContent ) ;
1109- domChatContact . classList . toggle ( 'chat-contact-with-status' , ! ! domChatContactStatus . textContent ) ;
1117+ const currentHasStatus = ! ! domChatContactStatus . textContent && ! domChatContactStatus . classList . contains ( 'status-hidden' ) ;
1118+ const newHasStatus = ! ! newStatusText ;
1119+
1120+ if ( newHasStatus ) {
1121+ // Show status: remove hidden class, update content
1122+ domChatContactStatus . classList . remove ( 'status-hidden' ) ;
1123+ domChatContactStatus . textContent = newStatusText ;
1124+ domChatContactStatus . classList . toggle ( 'text-gradient' , shouldAddGradient ) ;
1125+ if ( ! shouldAddGradient ) {
1126+ twemojify ( domChatContactStatus ) ;
1127+ }
1128+ domChatContact . classList . remove ( 'chat-contact' ) ;
1129+ domChatContact . classList . add ( 'chat-contact-with-status' ) ;
1130+ } else if ( currentHasStatus ) {
1131+ // Hide status: add hidden class, wait for animation, then clear content
1132+ domChatContactStatus . classList . add ( 'status-hidden' ) ;
1133+ domChatContact . classList . remove ( 'chat-contact-with-status' ) ;
1134+ domChatContact . classList . add ( 'chat-contact' ) ;
1135+
1136+ // Clear content after animation completes (300ms matches CSS transition)
1137+ statusHideTimeout = setTimeout ( ( ) => {
1138+ domChatContactStatus . textContent = '' ;
1139+ domChatContactStatus . classList . remove ( 'text-gradient' ) ;
1140+ statusHideTimeout = null ;
1141+ } , 300 ) ;
1142+ }
1143+ // If both are false (no status before, no status now), do nothing
11101144}
11111145
11121146// Store a hash of the last rendered state to detect actual changes
@@ -4575,6 +4609,13 @@ async function closeChat() {
45754609 previousChatBeforeProfile = "" ; // Clear when closing chat
45764610 nLastTypingIndicator = 0 ;
45774611
4612+ // Clear the chat header to prevent flicker when opening next chat
4613+ domChatContact . textContent = '' ;
4614+ domChatContactStatus . textContent = '' ;
4615+ domChatContactStatus . classList . add ( 'status-hidden' ) ;
4616+ domChatContactStatus . classList . remove ( 'text-gradient' ) ;
4617+ domChatHeaderAvatarContainer . innerHTML = '' ;
4618+
45784619 // Reset procedural scroll state
45794620 resetProceduralScroll ( ) ;
45804621
0 commit comments