Implement a viewer list

TODO:
- instead of fixing a size make it max-height
- when the chat is below the video it would probably be better to put the viewer list next to the chat
remotes/1694356327587836736/user-list
maia arson crimew 2021-11-13 16:21:01 +01:00
parent 903fd535ce
commit 98505de0f0
9 changed files with 59 additions and 7 deletions

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>watch party :D</title> <title>watch party :D</title>
<link rel="stylesheet" href="/styles.css?v=3" /> <link rel="stylesheet" href="/styles.css?v=4" />
</head> </head>
<body> <body>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>watch party :D</title> <title>watch party :D</title>
<link rel="stylesheet" href="/styles.css?v=3" /> <link rel="stylesheet" href="/styles.css?v=4" />
</head> </head>
<body> <body>
@ -46,12 +46,13 @@
<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..." />
</form> </form>
</div> </div>
<script type="module" src="/main.mjs?v=2"></script> <script type="module" src="/main.mjs?v=3"></script>
</body> </body>
</html> </html>

View File

@ -181,3 +181,19 @@ export const logEventToChat = (event) => {
} }
} }
}; };
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.appendChild(document.createTextNode(viewer));
viewerElem.appendChild(content);
listContainer.appendChild(viewerElem);
}
};

View File

@ -1,4 +1,4 @@
import { joinSession } from "./watch-session.mjs?v=3"; import { joinSession } from "./watch-session.mjs?v=4";
/** /**
* @param {HTMLInputElement} field * @param {HTMLInputElement} field

View File

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

View File

@ -1,4 +1,4 @@
import { setupJoinSessionForm } from "./lib/join-session.mjs?v=2"; import { setupJoinSessionForm } from "./lib/join-session.mjs?v=3";
const main = () => { const main = () => {
setupJoinSessionForm(); setupJoinSessionForm();

View File

@ -156,6 +156,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;
} }
@ -191,6 +201,6 @@ button.small-button {
} }
#chatbox { #chatbox {
height: calc(100vh - 5em) !important; height: calc(100vh - 5em - 4em) !important;
} }
} }

View File

@ -9,6 +9,7 @@ pub enum WatchEventData {
UserJoin, UserJoin,
UserLeave, UserLeave,
ChatMessage(String), ChatMessage(String),
UpdateViewerList(Vec<String>),
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]

View File

@ -65,6 +65,8 @@ pub async fn ws_subscribe(session_uuid: Uuid, nickname: String, ws: WebSocket) {
) )
.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()
@ -97,6 +99,7 @@ pub async fn ws_subscribe(session_uuid: Uuid, nickname: String, ws: WebSocket) {
.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) {
@ -111,3 +114,21 @@ pub async fn ws_publish(session_uuid: Uuid, skip_viewer_id: Option<usize>, event
}); });
} }
} }
async fn update_viewer_list(session_uuid: Uuid) {
let mut names = Vec::new();
for viewer in CONNECTED_VIEWERS.read().await.values() {
if viewer.session == session_uuid && viewer.nickname != None {
names.push(viewer.nickname.clone().unwrap())
}
}
ws_publish(
session_uuid,
None,
WatchEvent::new(
String::from("server"),
WatchEventData::UpdateViewerList(names),
),
)
.await;
}