mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-03 08:28:31 +00:00
4.9.5
* Added: Picture-in-Picture button for the player, when supported by your browser. * Added: Option to disable the `Reset Player` button for the player. * Fixed: Smooth scrolling not functioning once the chat buffer fills. It's still a bit peculiar, but at least it's somewhat working now. Please disable the feature for now if the behavior is bothering you or causing issues. * Fixed: The Featured channels menu not loading, for channels using that FFZ feature. * Fixed: Certain add-on emotes appearing in the emote tab-completion menu multiple times. * Fixed: Cheers not rendering correctly in chat.
This commit is contained in:
parent
9894e9a7cb
commit
512fe8898d
5 changed files with 166 additions and 12 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.9.4",
|
||||
"version": "4.9.5",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
|
|
@ -1846,6 +1846,8 @@ export default class ChatHook extends Module {
|
|||
if ( ! this.addRoom(cont, props) )
|
||||
return;
|
||||
|
||||
this.updateRoomBitsConfig(cont, props.bitsConfig);
|
||||
|
||||
if ( props.data ) {
|
||||
this.chat.badges.updateTwitchBadges(props.data.badges);
|
||||
this.updateRoomBadges(cont, props.data.user && props.data.user.broadcastBadges);
|
||||
|
@ -1864,6 +1866,9 @@ export default class ChatHook extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( props.bitsConfig !== cont.props.bitsConfig )
|
||||
this.updateRoomBitsConfig(cont, props.bitsConfig);
|
||||
|
||||
// Twitch, React, and Apollo are the trifecta of terror so we
|
||||
// can't compare the badgeSets property in any reasonable way.
|
||||
// Instead, just check the lengths to see if they've changed
|
||||
|
|
|
@ -29,7 +29,7 @@ export default class Scroller extends Module {
|
|||
|
||||
this.ChatScroller = this.fine.define(
|
||||
'chat-scroller',
|
||||
n => n.saveScrollRef && n.handleScrollEvent && ! n.renderLines,
|
||||
n => n.saveScrollRef && n.handleScrollEvent && ! n.renderLines && n.resume,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
|
@ -162,8 +162,23 @@ export default class Scroller extends Module {
|
|||
|
||||
this.ChatScroller.ready((cls, instances) => {
|
||||
const old_catch = cls.prototype.componentDidCatch,
|
||||
old_snapshot = cls.prototype.getSnapshotBeforeUpdate,
|
||||
old_render = cls.prototype.render;
|
||||
|
||||
if ( old_snapshot )
|
||||
cls.prototype.getSnapshotBeforeUpdate = function() {
|
||||
let auto_state;
|
||||
if ( this.state ) {
|
||||
auto_state = this.state.isAutoScrolling;
|
||||
this.state.isAutoScrolling = false;
|
||||
}
|
||||
const out = old_snapshot.call(this);
|
||||
if ( this.state )
|
||||
this.state.isAutoScrolling = auto_state;
|
||||
this._ffz_snapshot = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Try catching errors. With any luck, maybe we can
|
||||
// recover from the error when we re-build?
|
||||
cls.prototype.componentDidCatch = function(err, info) {
|
||||
|
@ -240,13 +255,23 @@ export default class Scroller extends Module {
|
|||
inst.smoothScrollBottom();
|
||||
else {
|
||||
inst._ffz_one_fast_scroll = false;
|
||||
inst._ffz_snapshot = null;
|
||||
inst.ffz_oldScroll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inst.scrollToBottom = function() {
|
||||
if ( inst._ffz_scroll_frame || inst.state.isPaused )
|
||||
// WIP: Trying to fix the scroll position changing so that we can
|
||||
// smooth scroll from the previous position.
|
||||
if ( inst.ffz_smooth_scroll && ! inst._ffz_one_fast_scroll && inst._ffz_snapshot ) {
|
||||
const adjustment = inst._ffz_snapshot && inst._ffz_snapshot.lastLine ? inst._ffz_snapshot.offsetTop - inst._ffz_snapshot.lastLine.offsetTop : 0;
|
||||
if ( inst.scroll && inst.scroll.scrollContent && adjustment > 0 )
|
||||
inst.scroll.scrollContent.scrollTop -= adjustment;
|
||||
}
|
||||
|
||||
inst._ffz_snapshot = null;
|
||||
if ( inst.state.isPaused || inst._ffz_scroll_frame )
|
||||
return;
|
||||
|
||||
this._ffz_scroll_frame = requestAnimationFrame(inst.ffz_doScroll);
|
||||
|
@ -592,7 +617,7 @@ export default class Scroller extends Module {
|
|||
} else if ( difference > 200 ) {
|
||||
// we are starting to fall behind, speed it up a bit
|
||||
step += step * Math.floor(difference / 200);
|
||||
}
|
||||
}
|
||||
|
||||
const smoothAnimation = () => {
|
||||
if ( this.state.isPaused || ! this.state.isAutoScrolling )
|
||||
|
|
|
@ -57,6 +57,35 @@ export default class Player extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('player.button.reset', {
|
||||
default: true,
|
||||
ui: {
|
||||
path: 'Player > General >> General',
|
||||
title: 'Add a `Reset Player` button to the player controls.',
|
||||
description: "Double-clicking the Reset Player button destroys and recreates Twitch's player, potentially fixing playback issues without a full page refresh.",
|
||||
component: 'setting-check-box'
|
||||
},
|
||||
changed: () => {
|
||||
for(const inst of this.Player.instances)
|
||||
this.addResetButton(inst);
|
||||
}
|
||||
});
|
||||
|
||||
if ( document.pictureInPictureEnabled )
|
||||
this.settings.add('player.button.pip', {
|
||||
default: true,
|
||||
ui: {
|
||||
path: 'Player > General >> General',
|
||||
title: 'Add a `Picture-in-Picture` button to the player controls.',
|
||||
description: "Clicking the PiP button attempts to toggle Picture-in-Picture mode for the player's video.",
|
||||
component: 'setting-check-box'
|
||||
},
|
||||
changed: () => {
|
||||
for(const inst of this.Player.instances)
|
||||
this.addPiPButton(inst);
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('player.volume-scroll-steps', {
|
||||
default: 0.1,
|
||||
ui: {
|
||||
|
@ -436,8 +465,10 @@ export default class Player extends Module {
|
|||
});
|
||||
|
||||
this.on('i18n:update', () => {
|
||||
for(const inst of this.Player.instances)
|
||||
for(const inst of this.Player.instances) {
|
||||
this.addPiPButton(inst);
|
||||
this.addResetButton(inst);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -493,6 +524,7 @@ export default class Player extends Module {
|
|||
|
||||
process(inst) {
|
||||
this.addResetButton(inst);
|
||||
this.addPiPButton(inst);
|
||||
this.addEndedListener(inst);
|
||||
this.addStateTags(inst);
|
||||
this.addControlVisibility(inst);
|
||||
|
@ -503,11 +535,23 @@ export default class Player extends Module {
|
|||
cleanup(inst) { // eslint-disable-line class-methods-use-this
|
||||
const p = inst.player,
|
||||
pr = inst.playerRef,
|
||||
reset = pr && pr.querySelector('.ffz--player-reset');
|
||||
video = pr && pr.querySelector('video'),
|
||||
reset = pr && pr.querySelector('.ffz--player-reset'),
|
||||
pip = pr && pr.querySelector('.ffz--player-pip');
|
||||
|
||||
if ( reset )
|
||||
reset.remove();
|
||||
|
||||
if ( pip )
|
||||
pip.remove();
|
||||
|
||||
if ( video && video._ffz_pip_enter ) {
|
||||
video.removeEventListener('enterpictureinpicture', video._ffz_pip_enter);
|
||||
video.removeEventListener('leavepictureinpicture', video._ffz_pip_exit);
|
||||
video._ffz_pip_enter = null;
|
||||
video._ffz_pip_exit = null;
|
||||
}
|
||||
|
||||
if ( inst._ffz_on_ended ) {
|
||||
p && off(p, 'ended', inst._ffz_on_ended);
|
||||
inst._ffz_on_ended = null;
|
||||
|
@ -709,11 +753,88 @@ export default class Player extends Module {
|
|||
}
|
||||
|
||||
|
||||
addPiPButton(inst, tries = 0) {
|
||||
const t = this,
|
||||
el = inst.playerRef && inst.playerRef.querySelector('.player-buttons-right .pl-flex'),
|
||||
container = el && el.parentElement,
|
||||
has_pip = document.pictureInPictureEnabled && this.settings.get('player.button.pip');
|
||||
|
||||
if ( ! container ) {
|
||||
if ( ! has_pip )
|
||||
return;
|
||||
|
||||
if ( tries < 5 )
|
||||
return setTimeout(this.addPiPButton.bind(this, inst, (tries||0) + 1), 250);
|
||||
|
||||
return this.log.warn('Unable to find container element for PiP Button');
|
||||
}
|
||||
|
||||
let tip, btn = container.querySelector('.ffz--player-pip');
|
||||
if ( ! has_pip ) {
|
||||
if ( btn )
|
||||
btn.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! btn ) {
|
||||
btn = (<button
|
||||
class="player-button player-button--pip ffz--player-pip ffz-i-window-restore"
|
||||
type="button"
|
||||
onClick={t.pipPlayer.bind(t, inst)} // eslint-disable-line react/jsx-no-bind
|
||||
>
|
||||
{tip = <span class="player-tip js-control-tip" />}
|
||||
</button>);
|
||||
|
||||
container.insertBefore(btn, el.nextSibling);
|
||||
|
||||
} else
|
||||
tip = btn.querySelector('.player-tip');
|
||||
|
||||
const pip_active = !!document.pictureInPictureElement
|
||||
|
||||
btn.classList.toggle('ffz-i-window-restore', ! pip_active);
|
||||
btn.classList.toggle('ffz-i-window-maximize', pip_active);
|
||||
|
||||
tip.dataset.tip = this.i18n.t('player.pip_button', 'Click to Toggle Picture-in-Picture');
|
||||
}
|
||||
|
||||
pipPlayer(inst) {
|
||||
const video = inst.playerRef && inst.playerRef.querySelector('video');
|
||||
if ( ! video || ! document.pictureInPictureEnabled )
|
||||
return;
|
||||
|
||||
if ( ! video._ffz_pip_enter ) {
|
||||
video.addEventListener('enterpictureinpicture', video._ffz_pip_enter = () => {
|
||||
this.addPiPButton(inst);
|
||||
});
|
||||
|
||||
video.addEventListener('leavepictureinpicture', video._ffz_pip_exit = () => {
|
||||
this.addPiPButton(inst);
|
||||
});
|
||||
}
|
||||
|
||||
if ( document.pictureInPictureElement )
|
||||
document.exitPictureInPicture();
|
||||
else
|
||||
video.requestPictureInPicture();
|
||||
}
|
||||
|
||||
|
||||
addResetButton(inst, tries = 0) {
|
||||
const t = this,
|
||||
el = inst.playerRef && inst.playerRef.querySelector('.player-buttons-right .pl-flex'),
|
||||
container = el && el.parentElement;
|
||||
|
||||
if ( ! this.settings.get('player.button.reset') ) {
|
||||
if ( container ) {
|
||||
const btn = container.querySelector('.ffz--player-reset');
|
||||
if ( btn )
|
||||
btn.remove();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! container ) {
|
||||
if ( tries < 5 )
|
||||
return setTimeout(this.addResetButton.bind(this, inst, (tries||0) + 1), 250);
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
.ffz--player-reset:hover:before {
|
||||
color: #a991d4;
|
||||
}
|
||||
.ffz--player-pip,
|
||||
.ffz--player-reset {
|
||||
&:before {
|
||||
font-size: 2rem;
|
||||
margin-top: .7rem;
|
||||
}
|
||||
|
||||
.ffz--player-reset:before {
|
||||
font-size: 2rem;
|
||||
margin-top: .7rem;
|
||||
&:hover:before {
|
||||
color: #a991d4;
|
||||
}
|
||||
}
|
||||
|
||||
.player-controls-bottom .player-tip {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue