1/** 2 * Per-user chatroom stats (message count, join date) in chatroom/userProfiles. 3 */ 4window.ChatroomUserStats = (function () { 5 'use strict'; 6 7 let profilesRef = null; 8 let currentUsername = null; 9 10 function init() { 11 if (typeof firebase === 'undefined' || !firebase.database) return false; 12 profilesRef = firebase.database().ref('chatroom/userProfiles'); 13 14 const check = setInterval(function () { 15 if (window.__sharedUsername) { 16 currentUsername = window.__sharedUsername.get(); 17 if (currentUsername) { 18 clearInterval(check); 19 ensureJoinedAt(currentUsername); 20 } 21 } 22 }, 100); 23 24 setTimeout(function () { 25 clearInterval(check); 26 if (!currentUsername && window.__sharedUsername) { 27 currentUsername = window.__sharedUsername.get(); 28 } 29 if (currentUsername) ensureJoinedAt(currentUsername); 30 }, 5000); 31 32 return true; 33 } 34 35 function ensureJoinedAt(username) { 36 if (!profilesRef || !username) return Promise.resolve(); 37 return profilesRef.child(username).transaction(function (current) { 38 const data = current && typeof current === 'object' ? current : {}; 39 if (data.joinedAt) return; 40 return Object.assign({}, data, { joinedAt: Date.now() }); 41 }).catch(function (err) { 42 console.warn('[ChatroomUserStats] ensureJoinedAt:', err); 43 }); 44 } 45 46 function incrementMessageCount(username) { 47 if (!profilesRef || !username) return; 48 profilesRef.child(username).transaction(function (current) { 49 const data = current && typeof current === 'object' ? current : {}; 50 const count = (data.messageCount || 0) + 1; 51 const next = Object.assign({}, data, { 52 messageCount: count, 53 joinedAt: data.joinedAt || Date.now() 54 }); 55 return next; 56 }).catch(function (err) { 57 console.warn('[ChatroomUserStats] increment:', err); 58 }); 59 } 60 61 function loadStats(username) { 62 if (!profilesRef || !username) { 63 return Promise.resolve({ messageCount: 0, joinedAt: null }); 64 } 65 return profilesRef.child(username).once('value').then(function (snap) { 66 const data = snap.exists() ? snap.val() : {}; 67 return { 68 messageCount: data.messageCount || 0, 69 joinedAt: data.joinedAt || null 70 }; 71 }).catch(function () { 72 return { messageCount: 0, joinedAt: null }; 73 }); 74 } 75 76 function formatJoinedDate(ts) { 77 if (!ts) return 'Unknown'; 78 try { 79 return new Date(ts).toLocaleDateString(undefined, { 80 year: 'numeric', 81 month: 'short', 82 day: 'numeric' 83 }); 84 } catch (e) { 85 return 'Unknown'; 86 } 87 } 88 89 if (document.readyState === 'loading') { 90 document.addEventListener('DOMContentLoaded', init); 91 } else { 92 init(); 93 } 94 95 return { 96 init: init, 97 ensureJoinedAt: ensureJoinedAt, 98 incrementMessageCount: incrementMessageCount, 99 loadStats: loadStats, 100 formatJoinedDate: formatJoinedDate 101 }; 102})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.