PageSourceSearch

https://pupwife.com/projects/chatroom/scripts/chatroomUserStats.js

js pupwife.com collected 2026-09-30 15:07:16 UTC 3,367 bytes, 102 lines download raw bytes

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.