Split activity status from session summary

This commit is contained in:
codex
2026-06-11 01:45:37 +02:00
parent 819cf0f121
commit 8dc0299b75
5 changed files with 63 additions and 14 deletions
+4 -2
View File
@@ -180,19 +180,21 @@ When KEEP-OPEN is nil, close PROCESS after writing."
(defun kkkmeet-server--remove-peer (process)
"Remove PROCESS from its room and notify remaining peers."
(let ((room-id (process-get process :kkkmeet-room-id))
(peer-id (process-get process :kkkmeet-peer-id)))
(peer-id (process-get process :kkkmeet-peer-id))
(name (or (process-get process :kkkmeet-name) "A participant")))
(when (and room-id peer-id)
(let ((room (gethash room-id kkkmeet-server--rooms)))
(when room
(remhash peer-id room)
(kkkmeet-server--broadcast
room-id peer-id `((type . "peer-left") (peerId . ,peer-id)))
room-id peer-id `((type . "peer-left") (peerId . ,peer-id) (name . ,name)))
(when (equal (alist-get 'peerId (gethash room-id kkkmeet-server--presentation-states))
peer-id)
(remhash room-id kkkmeet-server--presentation-states)
(kkkmeet-server--broadcast
room-id peer-id `((type . "presentation-state")
(from . ,peer-id)
(name . ,name)
(sharing . :false))))
(when (= (hash-table-count room) 0)
(remhash room-id kkkmeet-server--rooms)
+47 -10
View File
@@ -10,6 +10,7 @@ const nameDialogSubmit = document.querySelector("#name-dialog-submit");
const initialNameForm = document.querySelector("#initial-name-form");
const initialNameInput = document.querySelector("#initial-name-input");
const statusText = document.querySelector("#status");
const activityStatusText = document.querySelector("#activity-status");
const youtubeForm = document.querySelector("#youtube-form");
const youtubeInput = document.querySelector("#youtube-input");
const youtubeShell = document.querySelector("#youtube-shell");
@@ -73,6 +74,7 @@ let youtubeReadyResolvers = [];
let watchApplySequence = 0;
let youtubePlaybackRequestId = 0;
let watchPlaylist = [];
let remoteParticipantCount = 0;
const peers = new Map();
function persistentClientId() {
@@ -133,6 +135,26 @@ function setStatus(message) {
statusText.textContent = message;
}
function participantDisplayName(name, fallback = "Someone") {
return normalizeName(String(name || "")) || fallback;
}
function setSessionSummary() {
if (!peerId) {
setStatus(displayName ? `Ready as ${displayName}.` : "Enter your name to join the call.");
return;
}
const suffix = remoteParticipantCount === 1 ? "participant" : "participants";
setStatus(`You are ${displayName}. ${remoteParticipantCount} other ${suffix}.`);
}
function setActivity(name, action) {
const actor = participantDisplayName(name);
const message = `${actor} ${action}`;
activityStatusText.textContent = message;
console.info(`[kkkmeet activity] ${message}`);
}
function normalizeName(name) {
return name.trim().replace(/\s+/g, " ").slice(0, 48);
}
@@ -177,6 +199,7 @@ function setDisplayName(nextName) {
displayName = normalizeName(nextName);
localStorage.setItem(nameStorageKey, displayName);
localNameLabel.textContent = displayName ? `${displayName} (you)` : "You";
setSessionSummary();
}
function openNameDialog(mode) {
@@ -528,6 +551,7 @@ async function sendWatchState({
updatedAt: Date.now()
})
});
setActivity(displayName, "synced YouTube playback.");
}
function currentWatchState(playing) {
@@ -1150,7 +1174,9 @@ async function answerPeer(message) {
async function handleSignal(message) {
if (message.type === "welcome") {
peerId = message.peerId;
setStatus(`Joined the call as ${displayName}. ${message.peers.length} other participant(s).`);
remoteParticipantCount = message.peers.length;
setSessionSummary();
setActivity(displayName, "joined.");
if (message.watchState) {
const watchShouldPlay = message.watchState.action === "play" ||
(!message.watchState.action && Boolean(message.watchState.playing));
@@ -1175,13 +1201,20 @@ async function handleSignal(message) {
if (message.type === "peer-joined") {
updateRemoteLabel(message.peerId, message.name);
announceParticipantJoined(message.name);
setStatus(`${message.name || "A participant"} joined.`);
remoteParticipantCount += 1;
setSessionSummary();
setActivity(message.name, "joined.");
return;
}
if (message.type === "peer-left") {
const peerName = message.name ||
peers.get(message.peerId)?.name ||
`Participant ${message.peerId?.slice(0, 4) || ""}`.trim();
closePeer(message.peerId);
setStatus("A participant left.");
remoteParticipantCount = Math.max(0, remoteParticipantCount - 1);
setSessionSummary();
setActivity(peerName, "left.");
return;
}
@@ -1197,21 +1230,22 @@ async function handleSignal(message) {
allowJoinedYoutubeStartPrompt = false;
}
await applyWatchState(message, { allowStartPrompt });
setStatus(`${message.name || "Someone"} synced YouTube playback.`);
setActivity(message.name, "synced YouTube playback.");
return;
}
if (message.type === "presentation-state") {
setRemotePresentation(message.from, message.sharing, message.name);
setStatus(message.sharing
? `${message.name || "Someone"} is sharing their screen.`
: `${message.name || "Someone"} stopped sharing their screen.`);
setActivity(
message.name,
message.sharing ? "started sharing their screen." : "stopped sharing their screen."
);
return;
}
if (message.type === "voice-announcement") {
speakAnnouncement(message);
setStatus(`${message.name || "Someone"} sent an announcement.`);
setActivity(message.name, "sent an announcement.");
return;
}
@@ -1260,8 +1294,9 @@ function leaveCall() {
events?.close();
events = undefined;
peerId = "";
remoteParticipantCount = 0;
for (const remotePeerId of peers.keys()) closePeer(remotePeerId);
setStatus(displayName ? `Ready as ${displayName}.` : "Enter your name to join the call.");
setSessionSummary();
}
function setTrackEnabled(kind, enabled) {
@@ -1356,7 +1391,7 @@ async function sendVoiceAnnouncement(text) {
await sendRoomEvent({ type: "voice-announcement", text: announcementText });
speakAnnouncement({ text: announcementText });
setStatus("Announcement sent.");
setActivity(displayName, "sent an announcement.");
}
async function startScreenShare() {
@@ -1368,6 +1403,7 @@ async function startScreenShare() {
await replaceOutboundVideoTrack(screenTrack);
shareButton.textContent = "Stop Sharing";
await sendRoomEvent({ type: "presentation-state", sharing: true });
setActivity(displayName, "started sharing their screen.");
screenTrack.onended = () => {
stopScreenShare().catch((error) => {
@@ -1389,6 +1425,7 @@ async function stopScreenShare() {
}
shareButton.textContent = "Share Screen";
await sendRoomEvent({ type: "presentation-state", sharing: false });
setActivity(displayName, "stopped sharing their screen.");
}
initialNameForm.addEventListener("submit", (event) => {
+1
View File
@@ -12,6 +12,7 @@
<div>
<h1>kkkmeet</h1>
<p id="status">Enter your name to join the call.</p>
<p id="activity-status" aria-live="polite"></p>
</div>
<button id="rename-button" type="button">Rename</button>
</header>
+7
View File
@@ -62,6 +62,13 @@ h1 {
font-size: 14px;
}
#activity-status {
min-height: 18px;
margin-top: 3px;
color: #d5dbe3;
font-size: 13px;
}
#rename-button {
min-width: 92px;
}
+4 -2
View File
@@ -84,11 +84,13 @@ function removePeer(roomId, peerId) {
const room = rooms.get(roomId);
if (!room) return;
const peer = room.get(peerId);
const name = peer?.name || "A participant";
room.delete(peerId);
broadcast(roomId, peerId, { type: "peer-left", peerId });
broadcast(roomId, peerId, { type: "peer-left", peerId, name });
if (presentationStates.get(roomId)?.peerId === peerId) {
presentationStates.delete(roomId);
broadcast(roomId, peerId, { type: "presentation-state", from: peerId, sharing: false });
broadcast(roomId, peerId, { type: "presentation-state", from: peerId, name, sharing: false });
}
if (room.size === 0) {