From 2aaedd99b5f5c9d0bf7f9a5dead7a8ca40133ae5 Mon Sep 17 00:00:00 2001 From: Ginger Wong Date: Sat, 13 Jun 2020 23:38:09 -0700 Subject: [PATCH 1/3] set up localstorage-able items --- webroot/index.html | 20 +++++------- webroot/js/app.js | 58 +++++++++++++-------------------- webroot/js/message.js | 68 +++++++++++++++++++++++++++++++-------- webroot/styles/layout.css | 2 ++ 4 files changed, 88 insertions(+), 60 deletions(-) diff --git a/webroot/index.html b/webroot/index.html index 77b432c5c..245865e72 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -2,25 +2,20 @@ Live stream test - - + - - - + + + + + @@ -34,7 +29,7 @@
- Random Username 123 +
@@ -134,6 +129,7 @@
+ diff --git a/webroot/js/app.js b/webroot/js/app.js index 64f9f20f3..e9864a693 100644 --- a/webroot/js/app.js +++ b/webroot/js/app.js @@ -22,28 +22,29 @@ 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.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(); } @@ -109,16 +110,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..61014de8d 100644 --- a/webroot/js/message.js +++ b/webroot/js/message.js @@ -38,6 +38,8 @@ class Messaging { this.maxMessageLength = 500; this.maxMessageBuffer = 20; + this.keyUsername = "owncast_username"; + this.keyChatDisplayed = "owncast_chat"; this.tagChatToggle = document.querySelector("#chat-toggle"); @@ -47,7 +49,7 @@ class Messaging { 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"); @@ -58,7 +60,6 @@ class Messaging { this.btnCancelUpdateUsername = document.querySelector("#button-cancel-change"); this.formMessageInput = document.querySelector("#inputBody"); - } init() { this.tagChatToggle.addEventListener("click", this.handleChatToggle); @@ -69,16 +70,55 @@ class Messaging { this.inputChangeUserName.addEventListener("keydown", this.handleUsernameKeydown); this.formMessageInput.addEventListener("keydown", this.handleMessageInputKeydown); + this.initLocalStates(); + + var vueMessageForm = new Vue({ + el: "#message-form", + 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 = ""; + } + } + }); + + + } + + 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 = () => { - if (this.chatDisplayed) { - this.tagAppContainer.className = "flex no-chat"; - this.chatDisplayed = false; - } else { - this.tagAppContainer.className = "flex"; - this.chatDisplayed = true; - } + this.chatDisplayed = !this.chatDisplayed; + + setLocalStorage(this.keyChatDisplayed, this.chatDisplayed); + this.displayChat(); } handleShowChangeNameForm = () => { this.tagUserInfoDisplay.style.display = "none"; @@ -95,10 +135,12 @@ 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); + // this.inputChangeUserName.value = newValue; + // this.inputMessageAuthor.value = newValue; + // this.tagUsernameDisplay.innerText = newValue; + // this.imgUsernameAvatar.src = this.avatarSource + newValue; + setLocalStorage(this.keyUsername, newValue); } this.handleHideChangeNameForm(); } diff --git a/webroot/styles/layout.css b/webroot/styles/layout.css index 5ecc164b1..c72bf203c 100644 --- a/webroot/styles/layout.css +++ b/webroot/styles/layout.css @@ -148,10 +148,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 { From 0fa453044f4a0742042fc62a35464ca97f30dc58 Mon Sep 17 00:00:00 2001 From: Ginger Wong Date: Sun, 14 Jun 2020 00:24:26 -0700 Subject: [PATCH 2/3] fix chat; get rid of jquery --- webroot/index.html | 23 +++----------- webroot/js/app.js | 4 +-- webroot/js/message.js | 67 ++++++++++++++++++--------------------- webroot/js/utils.js | 32 +++++++++++++++++++ webroot/styles/layout.css | 3 ++ 5 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 webroot/js/utils.js diff --git a/webroot/index.html b/webroot/index.html index 245865e72..c4579b66d 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -10,9 +10,6 @@ - - - @@ -85,38 +82,28 @@ />

{{ message.author }}

-

+

-
+ - - - -
diff --git a/webroot/js/app.js b/webroot/js/app.js index e9864a693..8505bd9e6 100644 --- a/webroot/js/app.js +++ b/webroot/js/app.js @@ -84,8 +84,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"); } , 10); // could be better. is there a sort of Vue "componentDidUpdate" we can do this on? } } diff --git a/webroot/js/message.js b/webroot/js/message.js index 61014de8d..b69084e19 100644 --- a/webroot/js/message.js +++ b/webroot/js/message.js @@ -8,7 +8,6 @@ class Message { 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 - } + }; } } @@ -58,8 +57,9 @@ class Messaging { 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); @@ -70,30 +70,8 @@ class Messaging { this.inputChangeUserName.addEventListener("keydown", this.handleUsernameKeydown); this.formMessageInput.addEventListener("keydown", this.handleMessageInputKeydown); + this.btnSubmitMessage.addEventListener("click", this.handleSubmitChatButton); this.initLocalStates(); - - var vueMessageForm = new Vue({ - el: "#message-form", - 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 = ""; - } - } - }); - } @@ -136,10 +114,6 @@ class Messaging { if (newValue) { this.userName = newValue; this.updateUsernameFields(newValue); - // this.inputChangeUserName.value = newValue; - // this.inputMessageAuthor.value = newValue; - // this.tagUsernameDisplay.innerText = newValue; - // this.imgUsernameAvatar.src = this.avatarSource + newValue; setLocalStorage(this.keyUsername, newValue); } this.handleHideChangeNameForm(); @@ -160,11 +134,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; @@ -185,7 +157,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..d01eeb777 --- /dev/null +++ b/webroot/js/utils.js @@ -0,0 +1,32 @@ +function getLocalStorage(key) { + try { + return localStorage.getItem(key); + } catch (e) { + } + return null; +} + +function setLocalStorage(key, value) { + try { + localStorage.setItem(key, value); + return true; + } catch (e) {} + return false; +} + +function jumpToBottom(id) { + const div = document.querySelector(id); + console.log(div.scrollTop, div.scrollHeight , div.clientHeight) + 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 c72bf203c..583c4de44 100644 --- a/webroot/styles/layout.css +++ b/webroot/styles/layout.css @@ -198,6 +198,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; From 50a59ef3c0aa79733f905f6bfa1699cac9baafc7 Mon Sep 17 00:00:00 2001 From: Ginger Wong Date: Sun, 14 Jun 2020 01:10:26 -0700 Subject: [PATCH 3/3] more clean up and fixins --- webroot/index.html | 2 ++ webroot/js/app.js | 31 ++++++------------------------- webroot/js/message.js | 28 ++++++++++++++++------------ webroot/js/utils.js | 13 ++++++++++--- webroot/styles/layout.css | 19 ++++++++++++++----- 5 files changed, 48 insertions(+), 45 deletions(-) diff --git a/webroot/index.html b/webroot/index.html index c7693f1dd..6c8cbd092 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -1,5 +1,7 @@ + + Live stream test diff --git a/webroot/js/app.js b/webroot/js/app.js index 8ff70dd03..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,27 +24,6 @@ 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(); @@ -59,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." @@ -87,7 +68,7 @@ function setupWebsocket() { if (existing.length === 0 || !existing) { this.messagesContainer.messages.push(message); - setTimeout(() => { jumpToBottom("#messages-container"); } , 10); // could be better. is there a sort of Vue "componentDidUpdate" we can do this on? + setTimeout(() => { jumpToBottom("#messages-container"); } , 50); // could be better. is there a sort of Vue "componentDidUpdate" we can do this on? } } diff --git a/webroot/js/message.js b/webroot/js/message.js index b69084e19..9b31c0891 100644 --- a/webroot/js/message.js +++ b/webroot/js/message.js @@ -1,9 +1,9 @@ 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) { @@ -40,9 +40,11 @@ class Messaging { 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"); @@ -52,7 +54,6 @@ class Messaging { 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"); @@ -63,7 +64,7 @@ class Messaging { } 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); @@ -72,7 +73,7 @@ class Messaging { this.formMessageInput.addEventListener("keydown", this.handleMessageInputKeydown); this.btnSubmitMessage.addEventListener("click", this.handleSubmitChatButton); this.initLocalStates(); - + } initLocalStates() { @@ -94,16 +95,19 @@ class Messaging { handleChatToggle = () => { this.chatDisplayed = !this.chatDisplayed; - - setLocalStorage(this.keyChatDisplayed, this.chatDisplayed); + if (this.chatDisplayed) { + setLocalStorage(this.keyChatDisplayed, this.chatDisplayed); + } else { + 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 = () => { diff --git a/webroot/js/utils.js b/webroot/js/utils.js index d01eeb777..9bf1a4e5c 100644 --- a/webroot/js/utils.js +++ b/webroot/js/utils.js @@ -8,17 +8,24 @@ function getLocalStorage(key) { function setLocalStorage(key, value) { try { - localStorage.setItem(key, value); + 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); - console.log(div.scrollTop, div.scrollHeight , div.clientHeight) div.scrollTo({ - top: div.scrollHeight - div.clientHeight, + top: div.scrollHeight,// - div.clientHeight, left: 0, behavior: 'smooth' }); diff --git a/webroot/styles/layout.css b/webroot/styles/layout.css index 583c4de44..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; @@ -287,8 +294,10 @@ header h1 { overflow: hidden; } - - #info { + #user-info { + width: 12em; + } + #stream-info { display: none; overflow: auto; height: auto;