diff --git a/webroot/index.html b/webroot/index.html index 32c97a59e..4263caa4c 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -1,10 +1,3 @@ - - @@ -36,10 +29,10 @@ - + diff --git a/webroot/js/app.js b/webroot/js/app.js index d0151b8f8..56ff53b86 100644 --- a/webroot/js/app.js +++ b/webroot/js/app.js @@ -9,27 +9,31 @@ import Chat from './components/chat/chat.js'; import Websocket from './utils/websocket.js'; import { - getLocalStorage, - setLocalStorage, + addNewlines, + classNames, clearLocalStorage, + debounce, generateAvatar, generateUsername, - addNewlines, + getLocalStorage, pluralize, + setLocalStorage, } from './utils/helpers.js'; import { - URL_OWNCAST, - URL_CONFIG, - URL_STATUS, - TIMER_STATUS_UPDATE, - TIMER_DISABLE_CHAT_AFTER_OFFLINE, - TIMER_STREAM_DURATION_COUNTER, - TEMP_IMAGE, - MESSAGE_OFFLINE, - MESSAGE_ONLINE, - KEY_USERNAME, + HEIGHT_SHORT_WIDE, KEY_AVATAR, KEY_CHAT_DISPLAYED, + KEY_USERNAME, + MESSAGE_OFFLINE, + MESSAGE_ONLINE, + TEMP_IMAGE, + TIMER_DISABLE_CHAT_AFTER_OFFLINE, + TIMER_STATUS_UPDATE, + TIMER_STREAM_DURATION_COUNTER, + URL_CONFIG, + URL_OWNCAST, + URL_STATUS, + WIDTH_SINGLE_COL, } from './utils/constants.js'; export default class App extends Component { @@ -41,7 +45,8 @@ export default class App extends Component { displayChat: getLocalStorage(KEY_CHAT_DISPLAYED), // chat panel state chatEnabled: false, // chat input box state username: getLocalStorage(KEY_USERNAME) || generateUsername(), - userAvatarImage: getLocalStorage(KEY_AVATAR) || generateAvatar(`${this.username}${Date.now()}`), + userAvatarImage: + getLocalStorage(KEY_AVATAR) || generateAvatar(`${this.username}${Date.now()}`), configData: {}, extraUserContent: '', @@ -49,11 +54,15 @@ export default class App extends Component { playerActive: false, // player object is active streamOnline: false, // stream is active/online - //status + // status streamStatusMessage: MESSAGE_OFFLINE, viewerCount: '', sessionMaxViewerCount: '', overallMaxViewerCount: '', + + // dom + windowWidth: window.innerWidth, + windowHeight: window.innerHeight, }; // timers @@ -66,6 +75,7 @@ export default class App extends Component { // misc dom events this.handleChatPanelToggle = this.handleChatPanelToggle.bind(this); this.handleUsernameChange = this.handleUsernameChange.bind(this); + this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 400); this.handleOfflineMode = this.handleOfflineMode.bind(this); this.handleOnlineMode = this.handleOnlineMode.bind(this); @@ -81,12 +91,11 @@ export default class App extends Component { this.getConfig = this.getConfig.bind(this); this.getStreamStatus = this.getStreamStatus.bind(this); this.getExtraUserContent = this.getExtraUserContent.bind(this); - - } componentDidMount() { this.getConfig(); + window.addEventListener('resize', this.handleWindowResize); this.player = new OwncastPlayer(); this.player.setupPlayerCallbacks({ @@ -161,7 +170,6 @@ export default class App extends Component { }); } - setConfigData(data = {}) { const { title, extraUserInfoFileName, summary } = data; @@ -301,21 +309,30 @@ export default class App extends Component { console.log(`>>> App Error: ${error}`); } + handleWindowResize() { + this.setState({ + windowWidth: window.innerWidth, + windowHeight: window.innerHeight, + }); + } + render(props, state) { const { - username, - userAvatarImage, - websocket, + chatEnabled, configData, - extraUserContent, displayChat, - viewerCount, - sessionMaxViewerCount, + extraUserContent, overallMaxViewerCount, playerActive, + sessionMaxViewerCount, streamOnline, streamStatusMessage, - chatEnabled, + userAvatarImage, + username, + viewerCount, + websocket, + windowHeight, + windowWidth, } = state; const { @@ -347,14 +364,23 @@ export default class App extends Component { `); - - const chatClass = displayChat ? 'chat' : 'no-chat'; const mainClass = playerActive ? 'online' : ''; - const streamInfoClass = streamOnline ? 'online' : ''; + const streamInfoClass = streamOnline ? 'online' : ''; // need? + + const shortHeight = windowHeight <= HEIGHT_SHORT_WIDE; + const singleColMode = windowWidth <= WIDTH_SINGLE_COL && !shortHeight; + const extraAppClasses = classNames({ + 'chat': displayChat, + 'no-chat': !displayChat, + 'single-col': singleColMode, + 'bg-gray-800': singleColMode && displayChat, + 'short-wide': shortHeight, + }) + return ( html` -
-
+
+

-
${streamStatusMessage} ${viewerCount} ${pluralize('viewer', viewerCount)}. @@ -440,16 +465,15 @@ export default class App extends Component { Version ${appVersion} - - <${Chat} - websocket=${websocket} - username=${username} - userAvatarImage=${userAvatarImage} - chatEnabled=${chatEnabled} - /> - -
- `); + <${Chat} + websocket=${websocket} + username=${username} + userAvatarImage=${userAvatarImage} + chatEnabled //=${chatEnabled} + /> +
+ ` + ); } } diff --git a/webroot/js/components/chat/chat-input.js b/webroot/js/components/chat/chat-input.js index 0ba1e1cc1..b2482105a 100644 --- a/webroot/js/components/chat/chat-input.js +++ b/webroot/js/components/chat/chat-input.js @@ -232,7 +232,7 @@ export default class ChatInput extends Component { const placeholderText = generatePlaceholderText(inputEnabled, hasSentFirstChatMessage); return ( html` -
+
<${ContentEditable} id="message-input" diff --git a/webroot/js/components/chat/chat.js b/webroot/js/components/chat/chat.js index 4c4b486a3..2953de1b3 100644 --- a/webroot/js/components/chat/chat.js +++ b/webroot/js/components/chat/chat.js @@ -21,14 +21,11 @@ export default class Chat extends Component { this.scrollableMessagesContainer = createRef(); - this.websocket = null; this.getChatHistory = this.getChatHistory.bind(this); this.receivedWebsocketMessage = this.receivedWebsocketMessage.bind(this); this.websocketDisconnected = this.websocketDisconnected.bind(this); - - // this.handleSubmitChatButton = this.handleSubmitChatButton.bind(this); this.submitChat = this.submitChat.bind(this); } @@ -39,7 +36,6 @@ export default class Chat extends Component { if (hasTouchScreen()) { setVHvar(); window.addEventListener("orientationchange", setVHvar); - // this.tagAppContainer.classList.add('touch-screen'); } } diff --git a/webroot/js/components/chat/username.js b/webroot/js/components/chat/username.js index f18d37c4a..a2ba51711 100644 --- a/webroot/js/components/chat/username.js +++ b/webroot/js/components/chat/username.js @@ -22,8 +22,9 @@ export default class UsernameForm extends Component { } handleDisplayForm() { + const { displayForm: curDisplay } = this.state; this.setState({ - displayForm: true, + displayForm: !curDisplay, }); } @@ -65,7 +66,7 @@ export default class UsernameForm extends Component { const formDisplayStyle = narrowSpace ? 'inline-block' : 'flex'; const styles = { info: { - display: displayForm || narrowSpace ? 'none' : 'flex', + display: displayForm ? 'none' : 'flex', }, form: { display: displayForm ? formDisplayStyle : 'none', diff --git a/webroot/js/utils/constants.js b/webroot/js/utils/constants.js index a01cdb6c1..c83de5acd 100644 --- a/webroot/js/utils/constants.js +++ b/webroot/js/utils/constants.js @@ -27,3 +27,8 @@ export const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent'; export const CHAT_INITIAL_PLACEHOLDER_TEXT = 'Type here to chat, no account necessary.'; export const CHAT_PLACEHOLDER_TEXT = 'Message'; export const CHAT_PLACEHOLDER_OFFLINE = 'Chat is offline.'; + + +// app styling +export const WIDTH_SINGLE_COL = 730; +export const HEIGHT_SHORT_WIDE = 500; diff --git a/webroot/js/utils/helpers.js b/webroot/js/utils/helpers.js index 6962dfd61..1f913e8f9 100644 --- a/webroot/js/utils/helpers.js +++ b/webroot/js/utils/helpers.js @@ -128,3 +128,32 @@ export function classNames(json) { }); return classes.join(' '); } + + +// taken from +// https://medium.com/@TCAS3/debounce-deep-dive-javascript-es6-e6f8d983b7a1 +export function debounce(fn, time) { + let timeout; + + return function() { + const functionCall = () => fn.apply(this, arguments); + + clearTimeout(timeout); + timeout = setTimeout(functionCall, time); + } +} + +/* +const debouncedHandleResize = debounce(function handleResize() { + setDimensions({ + height: window.innerHeight, + width: window.innerWidth + }) +}, 1000) +window.addEventListener('resize', debouncedHandleResize) +window.addEventListener('keyup', debounce((e) => { + console.log(e); +}, 1000)); + +*/ + diff --git a/webroot/styles/app.css b/webroot/styles/app.css index c0b6d60c5..c361a19a6 100644 --- a/webroot/styles/app.css +++ b/webroot/styles/app.css @@ -1,6 +1,6 @@ /* -Spefici styles for app layout - +Specific styles for main app layout. +May have overrides for other components with own stylesheets. */ /* variables */ @@ -25,6 +25,10 @@ a:hover { background: transparent; } +* { + transition: all .25s; +} + button[disabled] { opacity: .5; pointer-events: none; @@ -68,11 +72,11 @@ header { /* ************************************************ */ #video-container { - height: calc(var(--video-container-height)); + height: var(--video-container-height); margin-top: var(--header-height); position: relative; width: 100%; - /* height: calc((9 / 16) * 100vw); */ + min-height: 480px; background-size: 30%; } @@ -97,13 +101,79 @@ header { opacity: .75; } +.no-chat #chat-container-wrap { + display: none; +} /* *********** overrides when chat is on ***************************** */ +.chat { + --content-width: calc(100vw - var(--right-col-width)); +} +.chat #chat-container-wrap { + display: block; +} + .chat #video-container, .chat #stream-info, .chat #user-content { - width: calc(100% - var(--right-col-width)); + width: var(--content-width); +} + +.chat #video-container { + height: calc((9 / 16) * var(--content-width)); +} + + + +.short-wide.chat #video-container { + height: calc(100vh - var(--header-height) - 3rem); + min-height: auto; +} + +.short-wide #message-input { + height: 3rem; +} + + + + +/* *********** single col layout ***************************** */ + +.single-col { + --right-col-width: 0px; +} +.single-col main { + position: fixed; + width: 100%; + z-index: 40; +} +.single-col #chat-container { + position: relative; + width: 100%; + height: auto; +} +.single-col #video-container { + min-height: auto; + width: 100%; +} +.single-col #user-content, +.single-col #chat-container-wrap { + margin-top: calc(var(--video-container-height) + var(--header-height) + 1rem); +} +.single-col #user-content .user-content { + flex-wrap: wrap; + justify-content: center; +} +.single-col.chat #user-content { + display: none; +} +.single-col #message-input-container { + width: 100%; +} + +.single-col #message-input { + height: 3rem; } @@ -116,15 +186,13 @@ header { --right-col-width: 20em; --user-image-width: 6em; } +} - #chat-container { - width: var(--right-col-width); - } +/* ************************************************8 */ -} /* single col layout */ -@media screen and (max-width: 640px ) { +/* @media screen and (max-width: 640px ) { :root { --right-col-width: 0; --video-container-height: 40vh; @@ -142,7 +210,6 @@ header { #chat-container { width: 100%; position: static; - /* min-height: calc(100vh - var(--header-height)); */ height: calc(100vh - var(--header-height) - var(--video-container-height) - 3vh) } #messages-container { @@ -166,16 +233,16 @@ header { .chat footer { display: none; } -} +} */ -@media screen and (max-height: 860px ) { +/* @media screen and (max-height: 860px ) { :root { --video-container-height: 40vh; } .user-content { flex-direction: column; } -} +} */ diff --git a/webroot/styles/chat.css b/webroot/styles/chat.css index 66334a20c..7dcf9af46 100644 --- a/webroot/styles/chat.css +++ b/webroot/styles/chat.css @@ -1,3 +1,5 @@ +/* some base styles for chat and messaging components */ + #chat-container { position: fixed; z-index: 9; @@ -8,20 +10,14 @@ height: calc(100vh - var(--header-height)); } -.touch-screen #chat-container { - height: calc(100vh - var(--header-height) - 3vh); -} - - -.no-chat #chat-container-wrap { - display: none; +#message-input-container { + width: var(--right-col-width); } -.chat #chat-container-wrap { - display: block; +#messages-container { + padding-bottom: 10rem; } - /******************************/ /******************************/ @@ -76,10 +72,6 @@ padding: 5px; } -.message-content { -} - - /* MESSAGE TEXT HTML */