diff --git a/webroot/index.html b/webroot/index.html index 28714cfed..6c8cbd092 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -1,26 +1,20 @@ + + Live stream test - - + - - - - - - + + + + + @@ -34,7 +28,7 @@
- Random Username 123 +
@@ -92,38 +86,28 @@ />

{{ message.author }}

-

+

-
+ - - - -
@@ -136,6 +120,7 @@
+ diff --git a/webroot/js/app.js b/webroot/js/app.js index 7b6bf8962..10e74fa23 100644 --- a/webroot/js/app.js +++ b/webroot/js/app.js @@ -12,6 +12,8 @@ function setupApp() { data: { streamStatus: "", viewerCount: 0, + sessionMaxViewerCount: 0, + overallMaxViewerCount: 0, }, }); @@ -22,28 +24,8 @@ function setupApp() { } }) - window.chatForm = new Vue({ - el: "#chatForm", - data: { - message: { - author: "",//localStorage.author || "Viewer" + (Math.floor(Math.random() * 42) + 1), - body: "" - } - }, - methods: { - submitChatForm: function (e) { - const message = new Message(this.message); - message.id = uuidv4(); - localStorage.author = message.author; - const messageJSON = JSON.stringify(message); - window.ws.send(messageJSON); - e.preventDefault(); - - this.message.body = ""; - } - } - }); + window.VIDEOJS_NO_DYNAMIC_STYLE = true; var appMessagingMisc = new Messaging(); appMessagingMisc.init(); } @@ -58,9 +40,9 @@ async function getStatus() { ? "Stream is online." : "Stream is offline." - app.viewerCount = status.viewerCount - app.sessionMaxViewerCount = status.sessionMaxViewerCount - app.overallMaxViewerCount = status.overallMaxViewerCount + app.viewerCount = status.viewerCount; + app.sessionMaxViewerCount = status.sessionMaxViewerCount; + app.overallMaxViewerCount = status.overallMaxViewerCount; } catch (e) { app.streamStatus = "Stream server is offline." @@ -85,8 +67,8 @@ function setupWebsocket() { }) if (existing.length === 0 || !existing) { - this.messagesContainer.messages.push(message) - scrollSmoothToBottom("messages-container") + this.messagesContainer.messages.push(message); + setTimeout(() => { jumpToBottom("#messages-container"); } , 50); // could be better. is there a sort of Vue "componentDidUpdate" we can do this on? } } @@ -111,16 +93,3 @@ getStatus() setupWebsocket() // setInterval(getStatus, 5000) -function scrollSmoothToBottom(id) { - const div = document.getElementById(id); - $('#' + id).animate({ - scrollTop: div.scrollHeight - div.clientHeight - }, 500) -} - -function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); -} diff --git a/webroot/js/message.js b/webroot/js/message.js index 5fc9d704c..9b31c0891 100644 --- a/webroot/js/message.js +++ b/webroot/js/message.js @@ -1,14 +1,13 @@ class Message { constructor(model) { - this.author = model.author - this.body = model.body - this.image = "https://robohash.org/" + model.author - this.id = model.id + this.author = model.author; + this.body = model.body; + this.image = model.image || "https://robohash.org/" + model.author; + this.id = model.id; } addNewlines(str) { return str.replace(/(?:\r\n|\r|\n)/g, '
'); - } formatText() { var linked = autoLink(this.body, { embed: true }); @@ -21,7 +20,7 @@ class Message { body: this.body(), image: this.image(), id: this.id - } + }; } } @@ -38,54 +37,77 @@ class Messaging { this.maxMessageLength = 500; this.maxMessageBuffer = 20; + this.keyUsername = "owncast_username"; + this.keyChatDisplayed = "owncast_chat"; + + this.tagAppContainer = document.querySelector("#app-container"); this.tagChatToggle = document.querySelector("#chat-toggle"); - this.tagUserInfoDisplay = document.querySelector("#user-info-display"); + this.textUserInfoDisplay = document.querySelector("#user-info-display"); this.tagUserInfoChanger = document.querySelector("#user-info-change"); this.tagUsernameDisplay = document.querySelector("#username-display"); this.imgUsernameAvatar = document.querySelector("#username-avatar"); - this.tagMessageAuthor = document.querySelector("#self-message-author"); + this.inputMessageAuthor = document.querySelector("#self-message-author"); this.tagMessageFormWarning = document.querySelector("#message-form-warning"); - this.tagAppContainer = document.querySelector("#app-container"); this.inputChangeUserName = document.querySelector("#username-change-input"); this.btnUpdateUserName = document.querySelector("#button-update-username"); this.btnCancelUpdateUsername = document.querySelector("#button-cancel-change"); + this.btnSubmitMessage = document.querySelector("#button-submit-message"); - this.formMessageInput = document.querySelector("#inputBody"); - + this.formMessageInput = document.querySelector("#message-body-form"); } init() { this.tagChatToggle.addEventListener("click", this.handleChatToggle); - this.tagUsernameDisplay.addEventListener("click", this.handleShowChangeNameForm); + this.textUserInfoDisplay.addEventListener("click", this.handleShowChangeNameForm); this.btnUpdateUserName.addEventListener("click", this.handleUpdateUsername); this.btnCancelUpdateUsername.addEventListener("click", this.handleHideChangeNameForm); this.inputChangeUserName.addEventListener("keydown", this.handleUsernameKeydown); this.formMessageInput.addEventListener("keydown", this.handleMessageInputKeydown); + this.btnSubmitMessage.addEventListener("click", this.handleSubmitChatButton); + this.initLocalStates(); + + } + + initLocalStates() { + this.username = getLocalStorage(this.keyUsername) || "User" + (Math.floor(Math.random() * 42) + 1); + this.updateUsernameFields(this.username); + + this.chatDisplayed = getLocalStorage(this.keyChatDisplayed) || false; + this.displayChat(); + } + updateUsernameFields(username) { + this.tagUsernameDisplay.innerText = username; + this.inputChangeUserName.value = username; + this.inputMessageAuthor.value = username; + this.imgUsernameAvatar.src = this.avatarSource + username; + } + displayChat() { + this.tagAppContainer.className = this.chatDisplayed ? "flex" : "flex no-chat"; } handleChatToggle = () => { + this.chatDisplayed = !this.chatDisplayed; if (this.chatDisplayed) { - this.tagAppContainer.className = "flex no-chat"; - this.chatDisplayed = false; + setLocalStorage(this.keyChatDisplayed, this.chatDisplayed); } else { - this.tagAppContainer.className = "flex"; - this.chatDisplayed = true; + clearLocalStorage(this.keyChatDisplayed); } + this.displayChat(); } handleShowChangeNameForm = () => { - this.tagUserInfoDisplay.style.display = "none"; + this.textUserInfoDisplay.style.display = "none"; this.tagUserInfoChanger.style.display = "flex"; } handleHideChangeNameForm = () => { - this.tagUserInfoDisplay.style.display = "flex"; + this.textUserInfoDisplay.style.display = "flex"; this.tagUserInfoChanger.style.display = "none"; } handleUpdateUsername = () => { @@ -95,10 +117,8 @@ class Messaging { if (newValue) { this.userName = newValue; - this.inputChangeUserName.value = newValue; - this.tagMessageAuthor.innerText = newValue; - this.tagUsernameDisplay.innerText = newValue; - this.imgUsernameAvatar.src = this.avatarSource + newValue; + this.updateUsernameFields(newValue); + setLocalStorage(this.keyUsername, newValue); } this.handleHideChangeNameForm(); } @@ -118,11 +138,9 @@ class Messaging { if (event.keyCode === 13) { // enter if (!this.prepNewLine) { - // submit() + this.submitChat(value); event.preventDefault(); - // clear out things. - this.formMessageInput.value = ""; - this.tagMessageFormWarning.innerText = ""; + return; } this.prepNewLine = false; @@ -143,7 +161,30 @@ class Messaging { this.tagMessageFormWarning.innerText = ""; } } + handleSubmitChatButton = event => { + var value = this.formMessageInput.value.trim(); + if (value) { + this.submitChat(value); + event.preventDefault(); + return false; + } + event.preventDefault(); + return false; + } + submitChat(content) { + if (!content) { + return; + } + var message = new Message({ + body: content, + author: this.username, + id: uuidv4(), + }); + const messageJSON = JSON.stringify(message); + window.ws.send(messageJSON); - - + // clear out things. + this.formMessageInput.value = ""; + this.tagMessageFormWarning.innerText = ""; + } } \ No newline at end of file diff --git a/webroot/js/utils.js b/webroot/js/utils.js new file mode 100644 index 000000000..9bf1a4e5c --- /dev/null +++ b/webroot/js/utils.js @@ -0,0 +1,39 @@ +function getLocalStorage(key) { + try { + return localStorage.getItem(key); + } catch (e) { + } + return null; +} + +function setLocalStorage(key, value) { + try { + if (value !== "" && value !== null) { + localStorage.setItem(key, value); + } else { + localStorage.removeItem(key); + } + return true; + } catch (e) {} + return false; +} + +function clearLocalStorage(key) { + localStorage.removeItem(key); +} + +function jumpToBottom(id) { + const div = document.querySelector(id); + div.scrollTo({ + top: div.scrollHeight,// - div.clientHeight, + left: 0, + behavior: 'smooth' + }); +} + +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} \ No newline at end of file diff --git a/webroot/styles/layout.css b/webroot/styles/layout.css index 5ecc164b1..4587e9d55 100644 --- a/webroot/styles/layout.css +++ b/webroot/styles/layout.css @@ -36,6 +36,7 @@ header { z-index: 10; flex-direction: row; justify-content: space-between; + flex-wrap: nowrap; } header h1 { @@ -46,6 +47,7 @@ header h1 { color: #ddd; padding: .5em; white-space: nowrap; + width: 20em; } #chat-toggle { @@ -53,7 +55,7 @@ header h1 { background-color: #555; text-align: center; height: 100%; - width: 3em; + min-width: 3em; justify-content: center; align-items: center; } @@ -67,6 +69,7 @@ header h1 { flex-direction: row; justify-content: flex-end; align-items: center; + flex-wrap: nowrap; } #user-info-display { @@ -76,6 +79,8 @@ header h1 { align-items: center; cursor: pointer; padding: .5em 1em; + overflow: hidden; + width: 100%; } #username-avatar { @@ -87,8 +92,10 @@ header h1 { #username-display { font-weight: bold; font-size: .75em; - color: #516FEB - + color: #516FEB; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } #user-info-display:hover { transition: opacity .2s; @@ -148,10 +155,12 @@ header h1 { flex-direction: column; justify-content: flex-start; align-items: center; + background-color: black; } #video-container video { width: 100%; display: block; + min-height: 100% } #stream-info { @@ -196,6 +205,9 @@ header h1 { align-items: flex-end; margin-bottom: 0; } +#message-body-form { + font-size: 1em; +} #message-form-actions { flex-direction: row; justify-content: space-between; @@ -282,8 +294,10 @@ header h1 { overflow: hidden; } - - #info { + #user-info { + width: 12em; + } + #stream-info { display: none; overflow: auto; height: auto;