forked from lavender/watch-party
implement a viewer list
parent
951007df2a
commit
72c212a100
|
@ -31,12 +31,7 @@
|
|||
/>
|
||||
|
||||
<label for="join-session-colour">Colour:</label>
|
||||
<input
|
||||
type="color"
|
||||
id="join-session-colour"
|
||||
value="#7ed0ff"
|
||||
required
|
||||
/>
|
||||
<input type="color" id="join-session-colour" value="#7ed0ff" required />
|
||||
|
||||
<label for="join-session-id">Session ID:</label>
|
||||
<input
|
||||
|
@ -55,6 +50,7 @@
|
|||
|
||||
<div id="video-container"></div>
|
||||
<div id="chatbox-container">
|
||||
<div id="viewer-list"></div>
|
||||
<div id="chatbox"></div>
|
||||
<form id="chatbox-send">
|
||||
<input type="text" placeholder="Message..." />
|
||||
|
|
|
@ -267,3 +267,20 @@ const beep = () => {
|
|||
oscillator.start(context.currentTime);
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
|
@ -70,6 +70,9 @@ const setupIncomingEvents = (video, socket) => {
|
|||
setDebounce();
|
||||
setVideoTime(event.data);
|
||||
break;
|
||||
case "UpdateViewerList":
|
||||
updateViewerList(event.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,6 +161,16 @@ button.small-button {
|
|||
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 {
|
||||
background-color: #222;
|
||||
}
|
||||
|
@ -196,6 +206,6 @@ button.small-button {
|
|||
}
|
||||
|
||||
#chatbox {
|
||||
height: calc(100vh - 5em) !important;
|
||||
height: calc(100vh - 5em - 4em) !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
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)]
|
||||
#[serde(tag = "op", content = "data")]
|
||||
pub enum WatchEventData {
|
||||
|
@ -17,6 +25,7 @@ pub enum WatchEventData {
|
|||
UserLeave,
|
||||
ChatMessage(String),
|
||||
Ping(String),
|
||||
UpdateViewerList(Vec<Viewer>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
|
|
@ -15,7 +15,7 @@ use uuid::Uuid;
|
|||
use warp::ws::{Message, WebSocket};
|
||||
|
||||
use crate::{
|
||||
events::{WatchEvent, WatchEventData},
|
||||
events::{Viewer, WatchEvent, WatchEventData},
|
||||
utils::truncate_str,
|
||||
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;
|
||||
|
||||
update_viewer_list(session_uuid).await;
|
||||
|
||||
while let Some(Ok(message)) = viewer_ws_rx.next().await {
|
||||
let event: WatchEventData = match message
|
||||
.to_str()
|
||||
|
@ -113,6 +115,7 @@ pub async fn ws_subscribe(session_uuid: Uuid, nickname: String, colour: String,
|
|||
.await;
|
||||
|
||||
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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue