forked from lavender/watch-party
		
	CSS updates (flexbox and theming) and emoji autocomplete
This commit is contained in:
		
							parent
							
								
									6d57cbc4a1
								
							
						
					
					
						commit
						1e73e0df72
					
				
					 5 changed files with 119 additions and 67 deletions
				
			
		|  | @ -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 { |   position: absolute; | ||||||
|     width: calc(100vw - 400px); |   bottom: 3.25rem; | ||||||
|     position: absolute; |   background-image: var(--autocomplete-bg); | ||||||
|     height: 100vh; |   padding: 0.25rem; | ||||||
|     background-color: black; |   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 a new issue