1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-03 08:28:31 +00:00
* 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:
SirStendec 2019-09-07 14:34:40 -04:00
parent 9894e9a7cb
commit 512fe8898d
5 changed files with 166 additions and 12 deletions

View file

@ -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": {

View file

@ -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

View file

@ -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 )

View file

@ -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);

View file

@ -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 {