changeset 73:a63faf49e1d7

last seen
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 08 Mar 2025 20:26:19 -0700
parents bce0480721c1
children 1c0336a7380f
files src/chat.js src/get_chat.js.luan src/heartbeat.js.luan src/lib/Online.luan src/lib/Shared.luan
diffstat 5 files changed, 103 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/chat.js	Sat Mar 08 17:29:57 2025 -0700
+++ b/src/chat.js	Sat Mar 08 20:26:19 2025 -0700
@@ -3,7 +3,7 @@
 let title = document.title;
 let currentChatId = null;
 let eventSource;
-let lastUpdate;
+let lastUpdate = Date.now();
 let userId;
 let filebinUrl;
 
@@ -227,28 +227,22 @@
 }
 
 function heartbeat() {
-	showOnline();
 	ajax(`heartbeat.js?last_update=${lastUpdate}`);
 }
 
 setInterval( heartbeat, 10000 );
+heartbeat();
 
 let online = {};
 
-function setOnline(userId) {
-	online[userId] = Date.now();
-}
-
 function showOnline() {
 	let old = Date.now() - 70000;
-	for( let id of Object.keys(online) ) {
-		if( online[id] < old )
-			delete online[id];
-	}
 	let spans = document.querySelectorAll('span[online]');
 	for( let span of spans ) {
 		let id = span.getAttribute('online');
-		span.setAttribute('is_online',!!online[id]);
+		let wasOnline = online[id];
+		let isOnline = !!wasOnline && wasOnline > old;
+		span.setAttribute('is_online',isOnline);
 	}
 }
 
@@ -328,7 +322,53 @@
 	reader.readAsArrayBuffer(file);
 }
 
+let times = [
+	{
+		time: 1000*60*60*24,
+		unit: 'day'
+	},
+	{
+		time: 1000*60*60,
+		unit: 'hour'
+	},
+	{
+		time: 1000*60,
+		unit: 'minute'
+	}
+];
+
+function ago(time) {
+	for( let t of times ) {
+		let n = Math.floor(time / t.time);
+		if( n > 0 ) {
+			let s = `${n} ${t.unit}`;
+			if( n > 1 )
+				s = s + 's';
+			return s + ' ago';
+		}
+	}
+	return 'just now';
+end
+}
+
 function openPeople() {
 	let dialog = document.querySelector('dialog[people]');
+	let spans = dialog.querySelectorAll('span[last_seen]');
+	let now = Date.now();
+	let old = now - 70000;
+	for( let span of spans ) {
+		let id = span.getAttribute('last_seen');
+		let s;
+		let lastOnline = online[id];
+		if( !lastOnline ) {
+			s = '';
+		} else if( lastOnline > old ) {
+			s = 'Active now';
+		} else {
+			s = 'Last seen ' + ago(now - lastOnline);
+		}
+		console.log(`${id} ${s}`);
+		span.textContent = s;
+	}
 	openModal(dialog);
 }
--- a/src/get_chat.js.luan	Sat Mar 08 17:29:57 2025 -0700
+++ b/src/get_chat.js.luan	Sat Mar 08 20:26:19 2025 -0700
@@ -68,21 +68,22 @@
 		local name = user.name
 		local email = user.email
 		local voice_url = user.voice_url
-		local voice = voice_url and `%> <a href="<%=voice_url%>" title="Call" target="voice"><img phone src="/images/call.svg"><%` or ""
+		local voice = voice_url and `%> <a href="<%=voice_url%>" title="Call" target="voice"><img phone src="/images/call.svg"></a><%` or ""
 %>
 		<p>
 <%
 		if name == nil then
 %>
-			<b><%=html_encode(email)%></b><%=voice%>
+			<b><%=html_encode(email)%></b><%=voice%><br>
 <%
 		else
 %>
 			<b><%=html_encode(name)%></b><%=voice%><br>
-			<%=html_encode(email)%>
+			<%=html_encode(email)%><br>
 <%
 		end
 %>
+			<span last_seen="<%=user_id%>"></span>
 		</p>
 <%
 	end
--- a/src/heartbeat.js.luan	Sat Mar 08 17:29:57 2025 -0700
+++ b/src/heartbeat.js.luan	Sat Mar 08 20:26:19 2025 -0700
@@ -2,6 +2,8 @@
 local error = Luan.error
 local String = require "luan:String.luan"
 local to_number = String.to_number or error()
+local Parsers = require "luan:Parsers.luan"
+local json_string = Parsers.json_string or error()
 local Time = require "luan:Time.luan"
 local time_now = Time.now or error()
 local Io = require "luan:Io.luan"
@@ -9,28 +11,33 @@
 local User = require "site:/lib/User.luan"
 local current_user = User.current or error()
 local Shared = require "site:/lib/Shared.luan"
-local http_push_to_users = Shared.http_push_to_users or error()
+local compressed = Shared.compressed or error()
+local Online = require "site:/lib/Online.luan"
+local set_online = Online.set or error()
+local get_online = Online.get or error()
 local Logging = require "luan:logging/Logging.luan"
 local logger = Logging.logger "heartbeat.js"
 
 
 return function()
 	local user = current_user() or error()
-	local user_ids = user.chatting_with_ids()
-	local js = "setOnline("..user.id..")"
-	http_push_to_users( user_ids, js )
+	set_online(user)
+	Io.stdout = Http.response.text_writer()
 
 	local last_update = Http.request.parameters.last_update or error()
 	last_update = to_number(last_update) or error(last_update)
 	local user_last_update = user.last_update()
-	local now = time_now()
-	if now - user_last_update < 10000 or last_update >= user_last_update then
-		-- logger.info "ok"
+	if time_now() - user_last_update > 70000 and last_update < user_last_update then
+		logger.info "update"
+%>
+		location = '/';
+<%
 		return
 	end
-	logger.info "update"
-	Io.stdout = Http.response.text_writer()
+
+	local online = get_online(user)
 %>
-	location = '/';
+	online = <%=json_string(online,compressed)%>;
+	showOnline();
 <%
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/Online.luan	Sat Mar 08 20:26:19 2025 -0700
@@ -0,0 +1,29 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local to_string = Luan.to_string or error()
+local Time = require "luan:Time.luan"
+local time_now = Time.now or error()
+local Thread = require "luan:Thread.luan"
+local User = require "site:/lib/User.luan"
+
+
+local Online = {}
+
+local glob = Thread.global_map("online")
+
+function Online.set(user)
+	glob[user.id] = time_now()
+end
+
+function Online.get(user)
+	local online = {}
+	local ids = user.chatting_with_ids()
+	ids[#ids+1] = user.id
+	for _, user_id in ipairs(ids) do
+		online[to_string(user_id)] = glob[user_id]
+	end
+	return online
+end
+
+return Online
--- a/src/lib/Shared.luan	Sat Mar 08 17:29:57 2025 -0700
+++ b/src/lib/Shared.luan	Sat Mar 08 20:26:19 2025 -0700
@@ -161,4 +161,6 @@
 	end
 end
 
+Shared.compressed = {compressed=true}
+
 return Shared