forked from lavender/watch-party
CSS updates (flexbox and theming) and emoji autocomplete
parent
6d57cbc4a1
commit
1e73e0df72
|
@ -53,7 +53,8 @@
|
||||||
<div id="viewer-list"></div>
|
<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... (/help for commands)" />
|
<input type="text" placeholder="Message... (/help for commands)" list="emoji-autocomplete" />
|
||||||
|
<div id="emoji-autocomplete"></div> <!-- DO NOT ADD SPACING INSIDE THE TAG IT WILL BREAK THE CSS kthxbye -->
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,40 @@
|
||||||
import { setDebounce, setVideoTime, setPlaying } from "./watch-session.mjs?v=9";
|
import { setDebounce, setVideoTime, setPlaying } from "./watch-session.mjs?v=9";
|
||||||
import { emojify } from "./emojis.mjs?v=9";
|
import { emojify, emojis } from "./emojis.mjs?v=9";
|
||||||
|
|
||||||
const setupChatboxEvents = (socket) => {
|
const setupChatboxEvents = (socket) => {
|
||||||
// clear events by just reconstructing the form
|
// clear events by just reconstructing the form
|
||||||
const oldChatForm = document.querySelector("#chatbox-send");
|
const oldChatForm = document.querySelector("#chatbox-send");
|
||||||
const chatForm = oldChatForm.cloneNode(true);
|
const chatForm = oldChatForm.cloneNode(true);
|
||||||
|
const messageInput = chatForm.querySelector("input");
|
||||||
|
const emojiAutocomplete = chatForm.querySelector("#emoji-autocomplete");
|
||||||
oldChatForm.replaceWith(chatForm);
|
oldChatForm.replaceWith(chatForm);
|
||||||
|
|
||||||
|
let autocompleting = false;
|
||||||
|
|
||||||
|
const replaceMessage = message => () => {
|
||||||
|
messageInput.value = message;
|
||||||
|
autocomplete();
|
||||||
|
}
|
||||||
|
async function autocomplete(){
|
||||||
|
if(autocompleting) return;
|
||||||
|
emojiAutocomplete.textContent = "";
|
||||||
|
autocompleting = true;
|
||||||
|
let text = messageInput.value.slice(0, messageInput.selectionStart);
|
||||||
|
const match = text.match(/(:[^\s:]+)?:[^\s:]*$/);
|
||||||
|
if(!match || match[1]) return autocompleting = false; // We don't need to autocomplete.
|
||||||
|
const prefix = text.slice(0, match.index);
|
||||||
|
const search = text.slice(match.index + 1);
|
||||||
|
const suffix = messageInput.value.slice(messageInput.selectionStart);
|
||||||
|
emojiAutocomplete.append(...(await emojis).filter(e => e.toLowerCase().startsWith(search.toLowerCase())).map(e => Object.assign(document.createElement("button"), {className: "emoji-option", textContent: e, onclick: replaceMessage(prefix + ":" + e + ":" + suffix)})))
|
||||||
|
autocompleting = false;
|
||||||
|
}
|
||||||
|
messageInput.addEventListener("input", autocomplete)
|
||||||
|
|
||||||
chatForm.addEventListener("submit", async (e) => {
|
chatForm.addEventListener("submit", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
const content = messageInput.value;
|
||||||
const input = chatForm.querySelector("input");
|
|
||||||
const content = input.value;
|
|
||||||
if (content.trim().length) {
|
if (content.trim().length) {
|
||||||
input.value = "";
|
messageInput.value = "";
|
||||||
|
|
||||||
// handle commands
|
// handle commands
|
||||||
if (content.startsWith("/")) {
|
if (content.startsWith("/")) {
|
||||||
|
@ -82,39 +103,20 @@ const setupChatboxEvents = (socket) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixChatSize = () => {
|
|
||||||
const video = document.querySelector("video");
|
|
||||||
const chatbox = document.querySelector("#chatbox");
|
|
||||||
const chatboxContainer = document.querySelector("#chatbox-container");
|
|
||||||
|
|
||||||
if (video && chatbox && chatboxContainer) {
|
|
||||||
const delta = chatboxContainer.clientHeight - chatbox.clientHeight;
|
|
||||||
|
|
||||||
chatbox.style["height"] = `calc(${
|
|
||||||
window.innerHeight - video.clientHeight
|
|
||||||
}px - ${delta}px - 1em)`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {WebSocket} socket
|
* @param {WebSocket} socket
|
||||||
*/
|
*/
|
||||||
export const setupChat = async (socket) => {
|
export const setupChat = async (socket) => {
|
||||||
document.querySelector("#chatbox-container").style["display"] = "block";
|
document.querySelector("#chatbox-container").style["display"] = "flex";
|
||||||
setupChatboxEvents(socket);
|
setupChatboxEvents(socket);
|
||||||
|
|
||||||
window.addEventListener("keydown", (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
try {
|
try {
|
||||||
const isSelectionEmpty = window.getSelection().toString().length === 0;
|
const isSelectionEmpty = window.getSelection().toString().length === 0;
|
||||||
if (event.code.match(/Key\w/) && isSelectionEmpty)
|
if (event.code.match(/Key\w/) && isSelectionEmpty)
|
||||||
document.querySelector("#chatbox-send > input").focus();
|
messageInput.focus();
|
||||||
} catch (_err) {}
|
} catch (_err) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
fixChatSize();
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
fixChatSize();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addToChat = (node) => {
|
const addToChat = (node) => {
|
||||||
|
@ -185,7 +187,7 @@ const printChatMessage = (eventType, user, colour, content) => {
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
const userName = document.createElement("strong");
|
const userName = document.createElement("strong");
|
||||||
userName.style = `color: #${colour}`;
|
userName.style = `--user-color: #${colour}`;
|
||||||
userName.textContent = user;
|
userName.textContent = user;
|
||||||
chatMessage.appendChild(userName);
|
chatMessage.appendChild(userName);
|
||||||
}
|
}
|
||||||
|
@ -327,7 +329,7 @@ export const updateViewerList = (viewers) => {
|
||||||
const viewerElem = document.createElement("div");
|
const viewerElem = document.createElement("div");
|
||||||
const content = document.createElement("strong");
|
const content = document.createElement("strong");
|
||||||
content.textContent = viewer.nickname;
|
content.textContent = viewer.nickname;
|
||||||
content.style = `color: #${viewer.colour}`;
|
content.style = `--user-color: #${viewer.colour}`;
|
||||||
viewerElem.appendChild(content);
|
viewerElem.appendChild(content);
|
||||||
listContainer.appendChild(viewerElem);
|
listContainer.appendChild(viewerElem);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,3 +9,4 @@ export function emojify(text) {
|
||||||
if(last < text.length) nodes.push(document.createTextNode(text.slice(last)))
|
if(last < text.length) nodes.push(document.createTextNode(text.slice(last)))
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
export const emojis = Promise.resolve(["blobcat", "blobhaj"])
|
|
@ -140,7 +140,9 @@ const createVideoElement = (videoUrl, subtitles) => {
|
||||||
export const setupVideo = async (videoUrl, subtitles, currentTime, playing) => {
|
export const setupVideo = async (videoUrl, subtitles, currentTime, playing) => {
|
||||||
document.querySelector("#pre-join-controls").style["display"] = "none";
|
document.querySelector("#pre-join-controls").style["display"] = "none";
|
||||||
const video = createVideoElement(videoUrl, subtitles);
|
const video = createVideoElement(videoUrl, subtitles);
|
||||||
document.querySelector("#video-container").appendChild(video);
|
const videoContainer = document.querySelector("#video-container");
|
||||||
|
videoContainer.style.display = "block";
|
||||||
|
videoContainer.appendChild(video);
|
||||||
|
|
||||||
video.currentTime = currentTime / 1000.0;
|
video.currentTime = currentTime / 1000.0;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
:root {
|
:root {
|
||||||
--bg: rgb(28, 23, 36);
|
--bg-rgb: 28, 23, 36;
|
||||||
--fg: rgb(234, 234, 248);
|
--fg-rgb: 234, 234, 248;
|
||||||
--accent: hsl(275, 57%, 68%);
|
--accent-rgb: 181, 127, 220;
|
||||||
|
--fg: rgb(var(--fg-rgb));
|
||||||
|
--bg: rgb(var(--bg-rgb));
|
||||||
|
--default-user-color: rgb(126, 208, 255);
|
||||||
|
--accent: rgb(var(--accent-rgb));
|
||||||
|
--fg-transparent: rgba(var(--fg-rgb), 0.125);
|
||||||
|
--bg-transparent: rgba(var(--bg-rgb), 0.125);
|
||||||
|
--chat-bg: linear-gradient(var(--fg-transparent), var(--fg-transparent)), linear-gradient(var(--bg), var(--bg));
|
||||||
|
--autocomplete-bg: linear-gradient(var(--fg-transparent), var(--fg-transparent)), linear-gradient(var(--fg-transparent), var(--fg-transparent)), linear-gradient(var(--bg), var(--bg));
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
@ -9,30 +17,34 @@ html {
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
|
||||||
overflow-y: scroll;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
display: block;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
width: 100vw;
|
#video-container {
|
||||||
height: auto;
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
max-width: auto;
|
display: none;
|
||||||
max-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -72,7 +84,7 @@ button {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
border: var(--accent);
|
border: var(--accent);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: #fff;
|
color: var(--fg);
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
@ -88,6 +100,7 @@ button {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border: 1px solid rgba(0, 0, 0, 0);
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.small-button {
|
button.small-button {
|
||||||
|
@ -133,8 +146,16 @@ button.small-button {
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message > strong {
|
.chat-message > strong, #viewer-list strong {
|
||||||
color: rgb(126, 208, 255);
|
color: var(--user-color, var(--default-user-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (-webkit-background-clip: text) {
|
||||||
|
.chat-message > strong, #viewer-list strong {
|
||||||
|
background: linear-gradient(var(--fg-transparent), var(--fg-transparent)), linear-gradient(var(--user-color, var(--default-user-color)), var(--user-color, var(--default-user-color)));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
color: transparent !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message.user-join,
|
.chat-message.user-join,
|
||||||
|
@ -168,27 +189,34 @@ button.small-button {
|
||||||
|
|
||||||
#chatbox {
|
#chatbox {
|
||||||
padding: 0.5em 2em;
|
padding: 0.5em 2em;
|
||||||
min-height: 8em;
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#viewer-list {
|
#viewer-list {
|
||||||
padding: 0.5em 2em;
|
padding: 0.5em 2em;
|
||||||
/* TODO: turn this into max-height instead of fixed height without breaking the chatbox height */
|
/* TODO: turn this into max-height instead of fixed height without breaking the chatbox height */
|
||||||
height: 4em;
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
color: rgb(126, 208, 255);
|
|
||||||
border-bottom: var(--fg);
|
border-bottom: var(--fg);
|
||||||
border-bottom-style: solid;
|
border-bottom-style: solid;
|
||||||
|
max-height: 4rem;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chatbox-container {
|
#chatbox-container {
|
||||||
background-color: #222;
|
background-image: var(--chat-bg);
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-basis: 400px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chatbox-send {
|
#chatbox-send {
|
||||||
padding: 0 2em;
|
padding: 0 2em;
|
||||||
padding-bottom: 0.5em;
|
padding-bottom: 0.5em;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chatbox-send > input {
|
#chatbox-send > input {
|
||||||
|
@ -196,22 +224,40 @@ button.small-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-aspect-ratio: 4/3) {
|
#emoji-autocomplete {
|
||||||
#video-container video {
|
|
||||||
width: calc(100vw - 400px);
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100vh;
|
bottom: 3.25rem;
|
||||||
background-color: black;
|
background-image: var(--autocomplete-bg);
|
||||||
}
|
padding: 0.25rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: calc(100% - 4.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
#video-container {
|
#emoji-autocomplete:empty {
|
||||||
float: left;
|
display: none;
|
||||||
height: 100vh;
|
}
|
||||||
position: relative;
|
|
||||||
|
.emoji-option {
|
||||||
|
background: transparent;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.emoji-option:hover, .emoji-option:focus {
|
||||||
|
background: var(--fg-transparent);
|
||||||
|
}
|
||||||
|
.emoji-option:last-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-aspect-ratio: 4/3) {
|
||||||
|
body {
|
||||||
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chatbox-container {
|
#chatbox-container {
|
||||||
float: right;
|
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 100vh !important;
|
height: 100vh !important;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue