misc issue fixes (#496)
* rename social-icons to prevent adblockers from blocking scripts, fix for #491 * hide viewer count when stream is off, fixes #428 * show a notice in document title of num messages if window is blurred, #426 * display indicator when stream has gone onlnie or offline when window is blurred
This commit is contained in:
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -223,7 +223,6 @@ export default class VideoOnly extends Component {
|
|||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
const { logo = TEMP_IMAGE } = configData;
|
const { logo = TEMP_IMAGE } = configData;
|
||||||
const streamInfoClass = streamOnline ? 'online' : ''; // need?
|
|
||||||
|
|
||||||
const mainClass = playerActive ? 'online' : '';
|
const mainClass = playerActive ? 'online' : '';
|
||||||
|
|
||||||
@@ -250,10 +249,10 @@ export default class VideoOnly extends Component {
|
|||||||
<section
|
<section
|
||||||
id="stream-info"
|
id="stream-info"
|
||||||
aria-label="Stream status"
|
aria-label="Stream status"
|
||||||
class="flex text-center flex-row justify-between font-mono py-2 px-8 bg-gray-900 text-indigo-200 shadow-md border-b border-gray-100 border-solid ${streamInfoClass}"
|
class="flex text-center flex-row justify-between font-mono py-2 px-8 bg-gray-900 text-indigo-200 shadow-md border-b border-gray-100 border-solid"
|
||||||
>
|
>
|
||||||
<span>${streamStatusMessage}</span>
|
<span>${streamStatusMessage}</span>
|
||||||
<span>${viewerCount} ${pluralize('viewer', viewerCount)}.</span>
|
<span id="stream-viewer-count">${viewerCount} ${pluralize('viewer', viewerCount)}.</span>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import htm from '/js/web_modules/htm.js';
|
|||||||
const html = htm.bind(h);
|
const html = htm.bind(h);
|
||||||
|
|
||||||
import { OwncastPlayer } from './components/player.js';
|
import { OwncastPlayer } from './components/player.js';
|
||||||
import SocialIconsList from './components/social-icons-list.js';
|
import SocialIconsList from './components/platform-logos-list.js';
|
||||||
import UsernameForm from './components/chat/username.js';
|
import UsernameForm from './components/chat/username.js';
|
||||||
import VideoPoster from './components/video-poster.js';
|
import VideoPoster from './components/video-poster.js';
|
||||||
import Chat from './components/chat/chat.js';
|
import Chat from './components/chat/chat.js';
|
||||||
@@ -44,6 +44,7 @@ export default class App extends Component {
|
|||||||
|
|
||||||
const chatStorage = getLocalStorage(KEY_CHAT_DISPLAYED);
|
const chatStorage = getLocalStorage(KEY_CHAT_DISPLAYED);
|
||||||
this.hasTouchScreen = hasTouchScreen();
|
this.hasTouchScreen = hasTouchScreen();
|
||||||
|
this.windowBlurred = false;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
websocket: new Websocket(),
|
websocket: new Websocket(),
|
||||||
@@ -81,6 +82,8 @@ export default class App extends Component {
|
|||||||
this.handleUsernameChange = this.handleUsernameChange.bind(this);
|
this.handleUsernameChange = this.handleUsernameChange.bind(this);
|
||||||
this.handleFormFocus = this.handleFormFocus.bind(this);
|
this.handleFormFocus = this.handleFormFocus.bind(this);
|
||||||
this.handleFormBlur = this.handleFormBlur.bind(this);
|
this.handleFormBlur = this.handleFormBlur.bind(this);
|
||||||
|
this.handleWindowBlur = this.handleWindowBlur.bind(this);
|
||||||
|
this.handleWindowFocus = this.handleWindowFocus.bind(this);
|
||||||
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 250);
|
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 250);
|
||||||
|
|
||||||
this.handleOfflineMode = this.handleOfflineMode.bind(this);
|
this.handleOfflineMode = this.handleOfflineMode.bind(this);
|
||||||
@@ -102,6 +105,8 @@ export default class App extends Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.getConfig();
|
this.getConfig();
|
||||||
window.addEventListener('resize', this.handleWindowResize);
|
window.addEventListener('resize', this.handleWindowResize);
|
||||||
|
window.addEventListener('blur', this.handleWindowBlur);
|
||||||
|
window.addEventListener('focus', this.handleWindowFocus);
|
||||||
if (this.hasTouchScreen) {
|
if (this.hasTouchScreen) {
|
||||||
window.addEventListener('orientationchange', this.handleWindowResize);
|
window.addEventListener('orientationchange', this.handleWindowResize);
|
||||||
}
|
}
|
||||||
@@ -123,6 +128,8 @@ export default class App extends Component {
|
|||||||
clearTimeout(this.disableChatTimer);
|
clearTimeout(this.disableChatTimer);
|
||||||
clearInterval(this.streamDurationTimer);
|
clearInterval(this.streamDurationTimer);
|
||||||
window.removeEventListener('resize', this.handleWindowResize);
|
window.removeEventListener('resize', this.handleWindowResize);
|
||||||
|
window.removeEventListener('blur', this.handleWindowBlur);
|
||||||
|
window.removeEventListener('focus', this.handleWindowFocus);
|
||||||
if (this.hasTouchScreen) {
|
if (this.hasTouchScreen) {
|
||||||
window.removeEventListener('orientationchange', this.handleWindowResize);
|
window.removeEventListener('orientationchange', this.handleWindowResize);
|
||||||
}
|
}
|
||||||
@@ -248,6 +255,10 @@ export default class App extends Component {
|
|||||||
if (this.player.vjsPlayer && this.player.vjsPlayer.paused()) {
|
if (this.player.vjsPlayer && this.player.vjsPlayer.paused()) {
|
||||||
this.handlePlayerEnded();
|
this.handlePlayerEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.windowBlurred) {
|
||||||
|
document.title = ` 🔴 ${this.state.configData && this.state.configData.title}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// play video!
|
// play video!
|
||||||
@@ -267,6 +278,10 @@ export default class App extends Component {
|
|||||||
chatInputEnabled: true,
|
chatInputEnabled: true,
|
||||||
streamStatusMessage: MESSAGE_ONLINE,
|
streamStatusMessage: MESSAGE_ONLINE,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.windowBlurred) {
|
||||||
|
document.title = ` 🟢 ${this.state.configData && this.state.configData.title}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentStreamDuration() {
|
setCurrentStreamDuration() {
|
||||||
@@ -335,6 +350,15 @@ export default class App extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleWindowBlur() {
|
||||||
|
this.windowBlurred = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWindowFocus() {
|
||||||
|
this.windowBlurred = false;
|
||||||
|
window.document.title = this.state.configData && this.state.configData.title;
|
||||||
|
}
|
||||||
|
|
||||||
render(props, state) {
|
render(props, state) {
|
||||||
const {
|
const {
|
||||||
chatInputEnabled,
|
chatInputEnabled,
|
||||||
@@ -381,7 +405,6 @@ export default class App extends Component {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const mainClass = playerActive ? 'online' : '';
|
const mainClass = playerActive ? 'online' : '';
|
||||||
const streamInfoClass = streamOnline ? 'online' : ''; // need?
|
|
||||||
const isPortrait = this.hasTouchScreen && orientation === ORIENTATION_PORTRAIT;
|
const isPortrait = this.hasTouchScreen && orientation === ORIENTATION_PORTRAIT;
|
||||||
const shortHeight = windowHeight <= HEIGHT_SHORT_WIDE && !isPortrait;
|
const shortHeight = windowHeight <= HEIGHT_SHORT_WIDE && !isPortrait;
|
||||||
const singleColMode = windowWidth <= WIDTH_SINGLE_COL && !shortHeight;
|
const singleColMode = windowWidth <= WIDTH_SINGLE_COL && !shortHeight;
|
||||||
@@ -462,10 +485,10 @@ export default class App extends Component {
|
|||||||
<section
|
<section
|
||||||
id="stream-info"
|
id="stream-info"
|
||||||
aria-label="Stream status"
|
aria-label="Stream status"
|
||||||
class="flex text-center flex-row justify-between font-mono py-2 px-8 bg-gray-900 text-indigo-200 shadow-md border-b border-gray-100 border-solid ${streamInfoClass}"
|
class="flex text-center flex-row justify-between font-mono py-2 px-8 bg-gray-900 text-indigo-200 shadow-md border-b border-gray-100 border-solid"
|
||||||
>
|
>
|
||||||
<span>${streamStatusMessage}</span>
|
<span>${streamStatusMessage}</span>
|
||||||
<span>${viewerCount} ${pluralize('viewer', viewerCount)}.</span>
|
<span id="stream-viewer-count">${viewerCount} ${pluralize('viewer', viewerCount)}.</span>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -514,6 +537,7 @@ export default class App extends Component {
|
|||||||
websocket=${websocket}
|
websocket=${websocket}
|
||||||
username=${username}
|
username=${username}
|
||||||
chatInputEnabled=${chatInputEnabled}
|
chatInputEnabled=${chatInputEnabled}
|
||||||
|
instanceTitle=${title}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ export default class Chat extends Component {
|
|||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
webSocketConnected: true,
|
|
||||||
messages: [],
|
|
||||||
chatUserNames: [],
|
chatUserNames: [],
|
||||||
|
messages: [],
|
||||||
newMessagesReceived: false,
|
newMessagesReceived: false,
|
||||||
|
webSocketConnected: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scrollableMessagesContainer = createRef();
|
this.scrollableMessagesContainer = createRef();
|
||||||
@@ -27,16 +27,20 @@ export default class Chat extends Component {
|
|||||||
this.websocket = null;
|
this.websocket = null;
|
||||||
this.receivedFirstMessages = false;
|
this.receivedFirstMessages = false;
|
||||||
|
|
||||||
|
this.windowBlurred = false;
|
||||||
|
this.numMessagesSinceBlur = 0;
|
||||||
|
|
||||||
this.getChatHistory = this.getChatHistory.bind(this);
|
this.getChatHistory = this.getChatHistory.bind(this);
|
||||||
|
this.handleNetworkingError = this.handleNetworkingError.bind(this);
|
||||||
|
this.handleWindowBlur = this.handleWindowBlur.bind(this);
|
||||||
|
this.handleWindowFocus = this.handleWindowFocus.bind(this);
|
||||||
|
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 500);
|
||||||
|
this.messageListCallback = this.messageListCallback.bind(this);
|
||||||
this.receivedWebsocketMessage = this.receivedWebsocketMessage.bind(this);
|
this.receivedWebsocketMessage = this.receivedWebsocketMessage.bind(this);
|
||||||
|
this.scrollToBottom = this.scrollToBottom.bind(this);
|
||||||
|
this.submitChat = this.submitChat.bind(this);
|
||||||
this.websocketConnected = this.websocketConnected.bind(this);
|
this.websocketConnected = this.websocketConnected.bind(this);
|
||||||
this.websocketDisconnected = this.websocketDisconnected.bind(this);
|
this.websocketDisconnected = this.websocketDisconnected.bind(this);
|
||||||
this.submitChat = this.submitChat.bind(this);
|
|
||||||
this.submitChat = this.submitChat.bind(this);
|
|
||||||
this.scrollToBottom = this.scrollToBottom.bind(this);
|
|
||||||
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 500);
|
|
||||||
this.handleNetworkingError = this.handleNetworkingError.bind(this);
|
|
||||||
this.messageListCallback = this.messageListCallback.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -45,6 +49,11 @@ export default class Chat extends Component {
|
|||||||
|
|
||||||
window.addEventListener('resize', this.handleWindowResize);
|
window.addEventListener('resize', this.handleWindowResize);
|
||||||
|
|
||||||
|
if (!this.props.messagesOnly) {
|
||||||
|
window.addEventListener('blur', this.handleWindowBlur);
|
||||||
|
window.addEventListener('focus', this.handleWindowFocus);
|
||||||
|
}
|
||||||
|
|
||||||
this.messageListObserver = new MutationObserver(this.messageListCallback);
|
this.messageListObserver = new MutationObserver(this.messageListCallback);
|
||||||
this.messageListObserver.observe(this.scrollableMessagesContainer.current, { childList: true });
|
this.messageListObserver.observe(this.scrollableMessagesContainer.current, { childList: true });
|
||||||
}
|
}
|
||||||
@@ -87,6 +96,10 @@ export default class Chat extends Component {
|
|||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleWindowResize);
|
window.removeEventListener('resize', this.handleWindowResize);
|
||||||
|
if (!this.props.messagesOnly) {
|
||||||
|
window.removeEventListener('blur', this.handleWindowBlur);
|
||||||
|
window.removeEventListener('focus', this.handleWindowFocus);
|
||||||
|
}
|
||||||
this.messageListObserver.disconnect();
|
this.messageListObserver.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +154,7 @@ export default class Chat extends Component {
|
|||||||
|
|
||||||
addMessage(message) {
|
addMessage(message) {
|
||||||
const { messages: curMessages } = this.state;
|
const { messages: curMessages } = this.state;
|
||||||
|
const { messagesOnly } = this.props;
|
||||||
|
|
||||||
// if incoming message has same id as existing message, don't add it
|
// if incoming message has same id as existing message, don't add it
|
||||||
const existing = curMessages.filter(function (item) {
|
const existing = curMessages.filter(function (item) {
|
||||||
@@ -157,6 +171,11 @@ export default class Chat extends Component {
|
|||||||
}
|
}
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if window is blurred and we get a new message, add 1 to title
|
||||||
|
if (!messagesOnly && message.type === 'CHAT' && this.windowBlurred) {
|
||||||
|
this.numMessagesSinceBlur += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
websocketConnected() {
|
websocketConnected() {
|
||||||
@@ -216,6 +235,16 @@ export default class Chat extends Component {
|
|||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleWindowBlur() {
|
||||||
|
this.windowBlurred = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWindowFocus() {
|
||||||
|
this.windowBlurred = false;
|
||||||
|
this.numMessagesSinceBlur = 0;
|
||||||
|
window.document.title = this.props.instanceTitle;
|
||||||
|
}
|
||||||
|
|
||||||
// if the messages list grows in number of child message nodes due to new messages received, scroll to bottom.
|
// if the messages list grows in number of child message nodes due to new messages received, scroll to bottom.
|
||||||
messageListCallback(mutations) {
|
messageListCallback(mutations) {
|
||||||
const numMutations = mutations.length;
|
const numMutations = mutations.length;
|
||||||
@@ -234,9 +263,18 @@ export default class Chat extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// update document title if window blurred
|
||||||
|
if (this.numMessagesSinceBlur && !this.props.messagesOnly && this.windowBlurred) {
|
||||||
|
this.updateDocumentTitle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateDocumentTitle() {
|
||||||
|
const num = this.numMessagesSinceBlur > 10 ? '10+' : this.numMessagesSinceBlur;
|
||||||
|
window.document.title = `${num} 💬 :: ${this.props.instanceTitle}`;
|
||||||
|
}
|
||||||
|
|
||||||
render(props, state) {
|
render(props, state) {
|
||||||
const { username, messagesOnly, chatInputEnabled } = props;
|
const { username, messagesOnly, chatInputEnabled } = props;
|
||||||
const { messages, chatUserNames, webSocketConnected } = state;
|
const { messages, chatUserNames, webSocketConnected } = state;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { h, Component } from '/js/web_modules/preact.js';
|
|||||||
import htm from '/js/web_modules/htm.js';
|
import htm from '/js/web_modules/htm.js';
|
||||||
const html = htm.bind(h);
|
const html = htm.bind(h);
|
||||||
|
|
||||||
import { SOCIAL_PLATFORMS } from '../utils/social.js';
|
import { SOCIAL_PLATFORMS } from '../utils/platforms.js';
|
||||||
import { classNames } from '../utils/helpers.js';
|
import { classNames } from '../utils/helpers.js';
|
||||||
|
|
||||||
function SocialIcon(props) {
|
function SocialIcon(props) {
|
||||||
@@ -60,4 +60,4 @@ export default function (props) {
|
|||||||
<span class="follow-label text-xs font-bold mr-2 uppercase">Follow me:</span>
|
<span class="follow-label text-xs font-bold mr-2 uppercase">Follow me:</span>
|
||||||
${list}
|
${list}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// x, y pixel psitions of /img/social.gif image.
|
// x, y pixel positions of /img/platform-logos.gif image.
|
||||||
export const SOCIAL_PLATFORMS = {
|
export const SOCIAL_PLATFORMS = {
|
||||||
default: {
|
default: {
|
||||||
name: "default",
|
name: "default",
|
||||||
@@ -75,6 +75,12 @@ header {
|
|||||||
#stream-info span {
|
#stream-info span {
|
||||||
font-size: .70rem;
|
font-size: .70rem;
|
||||||
}
|
}
|
||||||
|
#stream-viewer-count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.online #stream-viewer-count {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ************************************************ */
|
/* ************************************************ */
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--icon-width: 40px;
|
--icon-width: 40px;
|
||||||
height: var(--icon-width);
|
height: var(--icon-width);
|
||||||
width: var(--icon-width);
|
width: var(--icon-width);
|
||||||
background-image: url(/img/social-icons.gif);
|
background-image: url(/img/platform-logos.gif);
|
||||||
background-position: calc(var(--imgCol) * var(--icon-width)) calc(var(--imgRow) * var(--icon-width));
|
background-position: calc(var(--imgCol) * var(--icon-width)) calc(var(--imgRow) * var(--icon-width));
|
||||||
transform: scale(.65);
|
transform: scale(.65);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user