implement a viewer list

main
maia arson crimew 2022-02-13 18:23:20 +01:00
parent 951007df2a
commit 72c212a100
6 changed files with 72 additions and 10 deletions

View File

@ -31,12 +31,7 @@
/> />
<label for="join-session-colour">Colour:</label> <label for="join-session-colour">Colour:</label>
<input <input type="color" id="join-session-colour" value="#7ed0ff" required />
type="color"
id="join-session-colour"
value="#7ed0ff"
required
/>
<label for="join-session-id">Session ID:</label> <label for="join-session-id">Session ID:</label>
<input <input
@ -55,6 +50,7 @@
<div id="video-container"></div> <div id="video-container"></div>
<div id="chatbox-container"> <div id="chatbox-container">
<div id="viewer-list"></div>
<div id="chatbox"></div> <div id="chatbox"></div>
<form id="chatbox-send"> <form id="chatbox-send">
<input type="text" placeholder="Message..." /> <input type="text" placeholder="Message..." />

View File

@ -267,3 +267,20 @@ const beep = () => {
oscillator.start(context.currentTime); oscillator.start(context.currentTime);
oscillator.stop(context.currentTime + 0.22); oscillator.stop(context.currentTime + 0.22);
}; };
export const updateViewerList = (viewers) => {
const listContainer = document.querySelector("#viewer-list");
// empty out the current list
listContainer.innerHTML = "";
// display the updated list
for (const viewer of viewers) {
const viewerElem = document.createElement("div");
const content = document.createElement("strong");
content.textContent = viewer.nickname;
content.style = `color: #${viewer.colour}`;
viewerElem.appendChild(content);
listContainer.appendChild(viewerElem);
}
};

View File

@ -1,5 +1,5 @@
import { setupVideo } from "./video.mjs?v=8"; import { setupVideo } from "./video.mjs?v=8";
import { setupChat, logEventToChat } from "./chat.mjs?v=8"; import { setupChat, logEventToChat, updateViewerList } from "./chat.mjs?v=8";
/** /**
* @param {string} sessionId * @param {string} sessionId
@ -70,6 +70,9 @@ const setupIncomingEvents = (video, socket) => {
setDebounce(); setDebounce();
setVideoTime(event.data); setVideoTime(event.data);
break; break;
case "UpdateViewerList":
updateViewerList(event.data);
break;
} }
} }

View File

@ -161,6 +161,16 @@ button.small-button {
overflow-y: scroll; overflow-y: scroll;
} }
#viewer-list {
padding: 0.5em 2em;
/* TODO: turn this into max-height instead of fixed height without breaking the chatbox height */
height: 4em;
overflow-y: scroll;
color: rgb(126, 208, 255);
border-bottom: var(--fg);
border-bottom-style: solid;
}
#chatbox-container { #chatbox-container {
background-color: #222; background-color: #222;
} }
@ -196,6 +206,6 @@ button.small-button {
} }
#chatbox { #chatbox {
height: calc(100vh - 5em) !important; height: calc(100vh - 5em - 4em) !important;
} }
} }

View File

@ -1,5 +1,13 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct Viewer {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nickname: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub colour: Option<String>,
}
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
#[serde(tag = "op", content = "data")] #[serde(tag = "op", content = "data")]
pub enum WatchEventData { pub enum WatchEventData {
@ -17,6 +25,7 @@ pub enum WatchEventData {
UserLeave, UserLeave,
ChatMessage(String), ChatMessage(String),
Ping(String), Ping(String),
UpdateViewerList(Vec<Viewer>),
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]

View File

@ -15,7 +15,7 @@ use uuid::Uuid;
use warp::ws::{Message, WebSocket}; use warp::ws::{Message, WebSocket};
use crate::{ use crate::{
events::{WatchEvent, WatchEventData}, events::{Viewer, WatchEvent, WatchEventData},
utils::truncate_str, utils::truncate_str,
watch_session::{get_session, handle_watch_event_data}, watch_session::{get_session, handle_watch_event_data},
}; };
@ -74,6 +74,8 @@ pub async fn ws_subscribe(session_uuid: Uuid, nickname: String, colour: String,
) )
.await; .await;
update_viewer_list(session_uuid).await;
while let Some(Ok(message)) = viewer_ws_rx.next().await { while let Some(Ok(message)) = viewer_ws_rx.next().await {
let event: WatchEventData = match message let event: WatchEventData = match message
.to_str() .to_str()
@ -113,6 +115,7 @@ pub async fn ws_subscribe(session_uuid: Uuid, nickname: String, colour: String,
.await; .await;
CONNECTED_VIEWERS.write().await.remove(&viewer_id); CONNECTED_VIEWERS.write().await.remove(&viewer_id);
update_viewer_list(session_uuid).await;
} }
pub async fn ws_publish(session_uuid: Uuid, skip_viewer_id: Option<usize>, event: WatchEvent) { pub async fn ws_publish(session_uuid: Uuid, skip_viewer_id: Option<usize>, event: WatchEvent) {
@ -127,3 +130,27 @@ pub async fn ws_publish(session_uuid: Uuid, skip_viewer_id: Option<usize>, event
}); });
} }
} }
async fn update_viewer_list(session_uuid: Uuid) {
let mut viewers = Vec::new();
for viewer in CONNECTED_VIEWERS.read().await.values() {
if viewer.session == session_uuid {
viewers.push(Viewer {
nickname: viewer.nickname.clone(),
colour: viewer.colour.clone(),
})
}
}
ws_publish(
session_uuid,
None,
WatchEvent::new(
String::from("server"),
String::from(""),
WatchEventData::UpdateViewerList(viewers),
),
)
.await;
}