Fossil SCM
/chat now experimentally keeps track of the timestamp of the most recent message received from each user so that we can eventually integrate that information into the UI to provide a list of currently-active users (noting that we have no way of tracking the existence of lurkers).
Commit
be07b8d1373071d6af457eaa084fde43a4feef7176deab81a0e936bbe239e65d
Parent
994bec3637acb12…
1 file changed
+32
-3
+32
-3
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -121,10 +121,17 @@ | ||
| 121 | 121 | notificationBubbleColor: 'white', |
| 122 | 122 | totalMessageCount: 0, // total # of inbound messages |
| 123 | 123 | //! Number of messages to load for the history buttons |
| 124 | 124 | loadMessageCount: Math.abs(F.config.chat.initSize || 20), |
| 125 | 125 | ajaxInflight: 0, |
| 126 | + usersLastSeen:{ | |
| 127 | + /* Map of user names to their most recent message time | |
| 128 | + (JS Date object). Only messages received by the chat client | |
| 129 | + are considered. */ | |
| 130 | + /* Reminder: to convert a Julian time J to JS: | |
| 131 | + new Date((J - 2440587.5) * 86400000) */ | |
| 132 | + }, | |
| 126 | 133 | /** Gets (no args) or sets (1 arg) the current input text field value, |
| 127 | 134 | taking into account single- vs multi-line input. The getter returns |
| 128 | 135 | a string and the setter returns this object. */ |
| 129 | 136 | inputValue: function(){ |
| 130 | 137 | const e = this.inputElement(); |
| @@ -1079,17 +1086,33 @@ | ||
| 1079 | 1086 | the response from /chat-poll. If atEnd is true, the message is |
| 1080 | 1087 | appended to the end of the chat list (for loading older |
| 1081 | 1088 | messages), else the beginning (the default). */ |
| 1082 | 1089 | const newcontent = function f(jx,atEnd){ |
| 1083 | 1090 | if(!f.processPost){ |
| 1084 | - /** Processes chat message m, placing it either the start (if atEnd | |
| 1085 | - is falsy) or end (if atEnd is truthy) of the chat history. atEnd | |
| 1086 | - should only be true when loading older messages. */ | |
| 1091 | + /** Array.sort() callback. Expects an array of user names and | |
| 1092 | + sorts them in last-received message order (newest first). */ | |
| 1093 | + f.sortUsersSeen = function(l,r){ | |
| 1094 | + l = Chat.usersLastSeen[l]; | |
| 1095 | + r = Chat.usersLastSeen[r]; | |
| 1096 | + if(l && r) return r - l; | |
| 1097 | + else if(l) return -1; | |
| 1098 | + else if(r) return 1; | |
| 1099 | + else return 0; | |
| 1100 | + }; | |
| 1101 | + /** Processes chat message m, placing it either at the start (if | |
| 1102 | + atEnd is falsy) or end (if atEnd is truthy) of the chat | |
| 1103 | + history. atEnd should only be true when loading older | |
| 1104 | + messages. */ | |
| 1087 | 1105 | f.processPost = function(m,atEnd){ |
| 1088 | 1106 | ++Chat.totalMessageCount; |
| 1089 | 1107 | if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid; |
| 1090 | 1108 | if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid; |
| 1109 | + if(m.xfrom && m.mtime){ | |
| 1110 | + const d = new Date(m.mtime); | |
| 1111 | + const uls = Chat.usersLastSeen[m.xfrom]; | |
| 1112 | + if(!uls || uls<d) Chat.usersLastSeen[m.xfrom] = d; | |
| 1113 | + } | |
| 1091 | 1114 | if( m.mdel ){ |
| 1092 | 1115 | /* A record deletion notice. */ |
| 1093 | 1116 | Chat.deleteMessageElem(m.mdel); |
| 1094 | 1117 | return; |
| 1095 | 1118 | } |
| @@ -1098,10 +1121,16 @@ | ||
| 1098 | 1121 | } |
| 1099 | 1122 | const row = new Chat.MessageWidget(m); |
| 1100 | 1123 | Chat.injectMessageElem(row.e.body,atEnd); |
| 1101 | 1124 | if(m.isError){ |
| 1102 | 1125 | Chat._gotServerError = m; |
| 1126 | + }else if(false){ | |
| 1127 | + const users = Object.keys(Chat.usersLastSeen).sort(f.sortUsersSeen); | |
| 1128 | + console.debug("Users sorted by most recent activity (newest first):", users); | |
| 1129 | + users.forEach(function(u){ | |
| 1130 | + console.debug(u, Chat.usersLastSeen[u].toISOString()); | |
| 1131 | + }); | |
| 1103 | 1132 | } |
| 1104 | 1133 | }/*processPost()*/; |
| 1105 | 1134 | }/*end static init*/ |
| 1106 | 1135 | jx.msgs.forEach((m)=>f.processPost(m,atEnd)); |
| 1107 | 1136 | if('visible'===document.visibilityState){ |
| 1108 | 1137 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -121,10 +121,17 @@ | |
| 121 | notificationBubbleColor: 'white', |
| 122 | totalMessageCount: 0, // total # of inbound messages |
| 123 | //! Number of messages to load for the history buttons |
| 124 | loadMessageCount: Math.abs(F.config.chat.initSize || 20), |
| 125 | ajaxInflight: 0, |
| 126 | /** Gets (no args) or sets (1 arg) the current input text field value, |
| 127 | taking into account single- vs multi-line input. The getter returns |
| 128 | a string and the setter returns this object. */ |
| 129 | inputValue: function(){ |
| 130 | const e = this.inputElement(); |
| @@ -1079,17 +1086,33 @@ | |
| 1079 | the response from /chat-poll. If atEnd is true, the message is |
| 1080 | appended to the end of the chat list (for loading older |
| 1081 | messages), else the beginning (the default). */ |
| 1082 | const newcontent = function f(jx,atEnd){ |
| 1083 | if(!f.processPost){ |
| 1084 | /** Processes chat message m, placing it either the start (if atEnd |
| 1085 | is falsy) or end (if atEnd is truthy) of the chat history. atEnd |
| 1086 | should only be true when loading older messages. */ |
| 1087 | f.processPost = function(m,atEnd){ |
| 1088 | ++Chat.totalMessageCount; |
| 1089 | if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid; |
| 1090 | if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid; |
| 1091 | if( m.mdel ){ |
| 1092 | /* A record deletion notice. */ |
| 1093 | Chat.deleteMessageElem(m.mdel); |
| 1094 | return; |
| 1095 | } |
| @@ -1098,10 +1121,16 @@ | |
| 1098 | } |
| 1099 | const row = new Chat.MessageWidget(m); |
| 1100 | Chat.injectMessageElem(row.e.body,atEnd); |
| 1101 | if(m.isError){ |
| 1102 | Chat._gotServerError = m; |
| 1103 | } |
| 1104 | }/*processPost()*/; |
| 1105 | }/*end static init*/ |
| 1106 | jx.msgs.forEach((m)=>f.processPost(m,atEnd)); |
| 1107 | if('visible'===document.visibilityState){ |
| 1108 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -121,10 +121,17 @@ | |
| 121 | notificationBubbleColor: 'white', |
| 122 | totalMessageCount: 0, // total # of inbound messages |
| 123 | //! Number of messages to load for the history buttons |
| 124 | loadMessageCount: Math.abs(F.config.chat.initSize || 20), |
| 125 | ajaxInflight: 0, |
| 126 | usersLastSeen:{ |
| 127 | /* Map of user names to their most recent message time |
| 128 | (JS Date object). Only messages received by the chat client |
| 129 | are considered. */ |
| 130 | /* Reminder: to convert a Julian time J to JS: |
| 131 | new Date((J - 2440587.5) * 86400000) */ |
| 132 | }, |
| 133 | /** Gets (no args) or sets (1 arg) the current input text field value, |
| 134 | taking into account single- vs multi-line input. The getter returns |
| 135 | a string and the setter returns this object. */ |
| 136 | inputValue: function(){ |
| 137 | const e = this.inputElement(); |
| @@ -1079,17 +1086,33 @@ | |
| 1086 | the response from /chat-poll. If atEnd is true, the message is |
| 1087 | appended to the end of the chat list (for loading older |
| 1088 | messages), else the beginning (the default). */ |
| 1089 | const newcontent = function f(jx,atEnd){ |
| 1090 | if(!f.processPost){ |
| 1091 | /** Array.sort() callback. Expects an array of user names and |
| 1092 | sorts them in last-received message order (newest first). */ |
| 1093 | f.sortUsersSeen = function(l,r){ |
| 1094 | l = Chat.usersLastSeen[l]; |
| 1095 | r = Chat.usersLastSeen[r]; |
| 1096 | if(l && r) return r - l; |
| 1097 | else if(l) return -1; |
| 1098 | else if(r) return 1; |
| 1099 | else return 0; |
| 1100 | }; |
| 1101 | /** Processes chat message m, placing it either at the start (if |
| 1102 | atEnd is falsy) or end (if atEnd is truthy) of the chat |
| 1103 | history. atEnd should only be true when loading older |
| 1104 | messages. */ |
| 1105 | f.processPost = function(m,atEnd){ |
| 1106 | ++Chat.totalMessageCount; |
| 1107 | if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid; |
| 1108 | if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid; |
| 1109 | if(m.xfrom && m.mtime){ |
| 1110 | const d = new Date(m.mtime); |
| 1111 | const uls = Chat.usersLastSeen[m.xfrom]; |
| 1112 | if(!uls || uls<d) Chat.usersLastSeen[m.xfrom] = d; |
| 1113 | } |
| 1114 | if( m.mdel ){ |
| 1115 | /* A record deletion notice. */ |
| 1116 | Chat.deleteMessageElem(m.mdel); |
| 1117 | return; |
| 1118 | } |
| @@ -1098,10 +1121,16 @@ | |
| 1121 | } |
| 1122 | const row = new Chat.MessageWidget(m); |
| 1123 | Chat.injectMessageElem(row.e.body,atEnd); |
| 1124 | if(m.isError){ |
| 1125 | Chat._gotServerError = m; |
| 1126 | }else if(false){ |
| 1127 | const users = Object.keys(Chat.usersLastSeen).sort(f.sortUsersSeen); |
| 1128 | console.debug("Users sorted by most recent activity (newest first):", users); |
| 1129 | users.forEach(function(u){ |
| 1130 | console.debug(u, Chat.usersLastSeen[u].toISOString()); |
| 1131 | }); |
| 1132 | } |
| 1133 | }/*processPost()*/; |
| 1134 | }/*end static init*/ |
| 1135 | jx.msgs.forEach((m)=>f.processPost(m,atEnd)); |
| 1136 | if('visible'===document.visibilityState){ |
| 1137 |