mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-07 14:50:56 +00:00
4.17.15
* Added: Option to hide timestamps from Chat on Videos. * Added: TwitchCon and Overwatch League badges are now sorted into their own categories for controlling badge visibility. * Fixed: Automatically exit Picture-in-Picture when entering fullscreen mode. * Fixed: Do not let users enter Picture-in-Picture when already in fullscreen. * Fixed: Minimal Navigation not actually sliding the top navigation bar out of view.
This commit is contained in:
parent
829ec35808
commit
2d14c359c9
5 changed files with 82 additions and 16 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.17.14",
|
"version": "4.17.15",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -247,6 +247,8 @@ export default class Badges extends Module {
|
||||||
|
|
||||||
getSettingsBadges(include_addons) {
|
getSettingsBadges(include_addons) {
|
||||||
const twitch = [],
|
const twitch = [],
|
||||||
|
owl = [],
|
||||||
|
tcon = [],
|
||||||
game = [],
|
game = [],
|
||||||
ffz = [],
|
ffz = [],
|
||||||
addon = [];
|
addon = [];
|
||||||
|
@ -258,7 +260,7 @@ export default class Badges extends Module {
|
||||||
let v = badge && (badge[1] || badge[0]);
|
let v = badge && (badge[1] || badge[0]);
|
||||||
|
|
||||||
for(const key in badge)
|
for(const key in badge)
|
||||||
if ( has(badge, key) ) {
|
if ( key !== '__cat' && has(badge, key) ) {
|
||||||
const version = badge[key];
|
const version = badge[key];
|
||||||
if ( ! v )
|
if ( ! v )
|
||||||
v = version;
|
v = version;
|
||||||
|
@ -272,8 +274,18 @@ export default class Badges extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( v )
|
if ( v ) {
|
||||||
(badge.__game ? game : twitch).push({
|
let cat;
|
||||||
|
if ( badge.__cat === 'm-owl' )
|
||||||
|
cat = owl;
|
||||||
|
else if ( badge.__cat === 'm-tcon' )
|
||||||
|
cat = tcon;
|
||||||
|
else if ( badge.__cat === 'm-game' )
|
||||||
|
cat = game;
|
||||||
|
else
|
||||||
|
cat = twitch;
|
||||||
|
|
||||||
|
cat.push({
|
||||||
id: key,
|
id: key,
|
||||||
provider: 'twitch',
|
provider: 'twitch',
|
||||||
name: v.title,
|
name: v.title,
|
||||||
|
@ -282,6 +294,7 @@ export default class Badges extends Module {
|
||||||
versions: vs,
|
versions: vs,
|
||||||
styleImage: `url("${v.image2x}")`
|
styleImage: `url("${v.image2x}")`
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( include_addons )
|
if ( include_addons )
|
||||||
|
@ -302,6 +315,8 @@ export default class Badges extends Module {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{title: 'Twitch', id: 'm-twitch', badges: twitch},
|
{title: 'Twitch', id: 'm-twitch', badges: twitch},
|
||||||
|
{title: 'Twitch: TwitchCon', id: 'm-tcon', badges: tcon},
|
||||||
|
{title: 'Twitch: Overwatch League', id: 'm-owl', badges: owl},
|
||||||
{title: 'Twitch: Game', id: 'm-game', key: 'game', badges: game},
|
{title: 'Twitch: Game', id: 'm-game', key: 'game', badges: game},
|
||||||
{title: 'FrankerFaceZ', id: 'm-ffz', badges: ffz},
|
{title: 'FrankerFaceZ', id: 'm-ffz', badges: ffz},
|
||||||
{title: 'Add-on', id: 'm-addon', badges: addon}
|
{title: 'Add-on', id: 'm-addon', badges: addon}
|
||||||
|
@ -408,11 +423,11 @@ export default class Badges extends Module {
|
||||||
is_colored = badge_style !== 5,
|
is_colored = badge_style !== 5,
|
||||||
has_image = badge_style !== 3 && badge_style !== 4,
|
has_image = badge_style !== 3 && badge_style !== 4,
|
||||||
|
|
||||||
twitch_hidden = hidden_badges['m-twitch'],
|
|
||||||
game_hidden = hidden_badges['m-game'],
|
|
||||||
ffz_hidden = hidden_badges['m-ffz'],
|
ffz_hidden = hidden_badges['m-ffz'],
|
||||||
addon_hidden = hidden_badges['m-addon'],
|
addon_hidden = hidden_badges['m-addon'],
|
||||||
|
|
||||||
|
tb = this.twitch_badges,
|
||||||
|
|
||||||
out = [],
|
out = [],
|
||||||
slotted = {},
|
slotted = {},
|
||||||
twitch_badges = msg.badges || {},
|
twitch_badges = msg.badges || {},
|
||||||
|
@ -433,9 +448,10 @@ export default class Badges extends Module {
|
||||||
if ( has(twitch_badges, badge_id) ) {
|
if ( has(twitch_badges, badge_id) ) {
|
||||||
const version = twitch_badges[badge_id],
|
const version = twitch_badges[badge_id],
|
||||||
is_hidden = hidden_badges[badge_id],
|
is_hidden = hidden_badges[badge_id],
|
||||||
is_game = badge_id.endsWith('_1');
|
bdata = tb && tb[badge_id],
|
||||||
|
cat = bdata && bdata.__cat || 'm-twitch';
|
||||||
|
|
||||||
if ( is_hidden || (is_hidden == null && (is_game ? game_hidden : twitch_hidden)))
|
if ( is_hidden || (is_hidden == null && hidden_badges[cat]) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( has(BADGE_POSITIONS, badge_id) )
|
if ( has(BADGE_POSITIONS, badge_id) )
|
||||||
|
@ -771,7 +787,9 @@ export default class Badges extends Module {
|
||||||
b = {};
|
b = {};
|
||||||
for(const data of badges) {
|
for(const data of badges) {
|
||||||
const sid = data.setID,
|
const sid = data.setID,
|
||||||
bs = b[sid] = b[sid] || {__game: /_\d+$/.test(sid) && ! sid.includes('overwatch') };
|
bs = b[sid] = b[sid] || {
|
||||||
|
__cat: getBadgeCategory(sid)
|
||||||
|
};
|
||||||
|
|
||||||
this.twitch_badge_count++;
|
this.twitch_badge_count++;
|
||||||
bs[data.version] = data;
|
bs[data.version] = data;
|
||||||
|
@ -864,4 +882,16 @@ export default class Badges extends Module {
|
||||||
else
|
else
|
||||||
this.style.delete('twitch-badges');
|
this.style.delete('twitch-badges');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getBadgeCategory(key) {
|
||||||
|
if ( key.startsWith('overwatch-league') )
|
||||||
|
return 'm-owl';
|
||||||
|
else if ( key.startsWith('twitchcon') )
|
||||||
|
return 'm-tcon';
|
||||||
|
else if ( /_\d+$/.test(key) )
|
||||||
|
return 'm-game';
|
||||||
|
|
||||||
|
return 'm-twitch';
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
height: 5rem !important;
|
height: 5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position: relative;
|
||||||
height: 1rem !important;
|
height: 1rem !important;
|
||||||
top: -4rem !important;
|
top: -4rem !important;
|
||||||
|
|
||||||
|
|
|
@ -759,10 +759,22 @@ export default class Player extends Module {
|
||||||
|
|
||||||
installVisibilityHook() {
|
installVisibilityHook() {
|
||||||
if ( ! document.pictureInPictureEnabled ) {
|
if ( ! document.pictureInPictureEnabled ) {
|
||||||
this.log.info('Skipping visibility hook. Picture-in-Picture is not available.');
|
this.log.info('Skipping visibility hooks. Picture-in-Picture is not available.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('fullscreenchange', () => {
|
||||||
|
const fs = document.fullscreenElement,
|
||||||
|
pip = document.pictureInPictureElement;
|
||||||
|
|
||||||
|
if ( fs && pip && (fs === pip || fs.contains(pip)) )
|
||||||
|
document.exitPictureInPicture();
|
||||||
|
|
||||||
|
// Update the UI since we can't enter PiP from Fullscreen
|
||||||
|
for(const inst of this.Player.instances)
|
||||||
|
this.addPiPButton(inst);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(document, 'hidden', {
|
Object.defineProperty(document, 'hidden', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
@ -796,6 +808,8 @@ export default class Player extends Module {
|
||||||
|
|
||||||
addPiPButton(inst, tries = 0) {
|
addPiPButton(inst, tries = 0) {
|
||||||
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
|
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
|
||||||
|
video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video,
|
||||||
|
is_fs = video && document.fullscreenElement && document.fullscreenElement.contains(video),
|
||||||
container = outer && outer.querySelector('.player-controls__right-control-group'),
|
container = outer && outer.querySelector('.player-controls__right-control-group'),
|
||||||
has_pip = document.pictureInPictureEnabled && this.settings.get('player.button.pip');
|
has_pip = document.pictureInPictureEnabled && this.settings.get('player.button.pip');
|
||||||
|
|
||||||
|
@ -846,12 +860,17 @@ export default class Player extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pip_active = !!document.pictureInPictureElement,
|
const pip_active = !!document.pictureInPictureElement,
|
||||||
label = pip_active ?
|
pip_swap = false, //pip_active && document.pictureInPictureElement !== video,
|
||||||
this.i18n.t('player.pip_button.off', 'Exit Picture-in-Picture') :
|
label = is_fs ?
|
||||||
this.i18n.t('player.pip_button', 'Picture-in-Picture');
|
this.i18n.t('player.pip_button.fs', 'Cannot use Picture-in-Picture when Fullscreen')
|
||||||
|
: pip_swap ?
|
||||||
|
this.i18n.t('player.pip_button.swap', 'Switch Picture-in-Picture')
|
||||||
|
: pip_active ?
|
||||||
|
this.i18n.t('player.pip_button.off', 'Exit Picture-in-Picture')
|
||||||
|
: this.i18n.t('player.pip_button', 'Picture-in-Picture');
|
||||||
|
|
||||||
icon.classList.toggle('ffz-i-t-pip-inactive', ! pip_active);
|
icon.classList.toggle('ffz-i-t-pip-inactive', ! pip_active || pip_swap);
|
||||||
icon.classList.toggle('ffz-i-t-pip-active', pip_active);
|
icon.classList.toggle('ffz-i-t-pip-active', pip_active && ! pip_swap);
|
||||||
|
|
||||||
btn.setAttribute('aria-label', label);
|
btn.setAttribute('aria-label', label);
|
||||||
tip.textContent = label;
|
tip.textContent = label;
|
||||||
|
@ -866,6 +885,9 @@ export default class Player extends Module {
|
||||||
if ( e )
|
if ( e )
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
if ( document.fullscreenElement && document.fullscreenElement.contains(video) )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! video._ffz_pip_enter ) {
|
if ( ! video._ffz_pip_enter ) {
|
||||||
video.addEventListener('enterpictureinpicture', video._ffz_pip_enter = () => {
|
video.addEventListener('enterpictureinpicture', video._ffz_pip_enter = () => {
|
||||||
this.addPiPButton(inst);
|
this.addPiPButton(inst);
|
||||||
|
@ -876,9 +898,11 @@ export default class Player extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//const is_this = document.pictureInPictureElement === video;
|
||||||
if ( document.pictureInPictureElement )
|
if ( document.pictureInPictureElement )
|
||||||
document.exitPictureInPicture();
|
document.exitPictureInPicture();
|
||||||
else
|
else
|
||||||
|
//if ( ! is_this )
|
||||||
video.requestPictureInPicture();
|
video.requestPictureInPicture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,15 @@ export default class VideoChatHook extends Module {
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
|
|
||||||
|
this.settings.add('chat.video-chat.timestamps', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Chat on Videos >> Appearance',
|
||||||
|
title: 'Display timestamps alongside chat messages.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.video-chat.enabled', {
|
this.settings.add('chat.video-chat.enabled', {
|
||||||
default: true,
|
default: true,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -64,6 +73,7 @@ export default class VideoChatHook extends Module {
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
this.chat.context.on('changed:chat.video-chat.enabled', this.updateLines, this);
|
this.chat.context.on('changed:chat.video-chat.enabled', this.updateLines, this);
|
||||||
|
this.chat.context.on('changed:chat.video-chat.timestamps', this.updateLines, this);
|
||||||
this.on('chat:updated-lines', this.updateLines, this);
|
this.on('chat:updated-lines', this.updateLines, this);
|
||||||
|
|
||||||
this.VideoChatController.on('mount', this.chatMounted, this);
|
this.VideoChatController.on('mount', this.chatMounted, this);
|
||||||
|
@ -275,6 +285,7 @@ export default class VideoChatHook extends Module {
|
||||||
const context = this.props.messageContext,
|
const context = this.props.messageContext,
|
||||||
msg = t.standardizeMessage(context.comment, context.author),
|
msg = t.standardizeMessage(context.comment, context.author),
|
||||||
main_message = this.ffzRenderMessage(msg, context),
|
main_message = this.ffzRenderMessage(msg, context),
|
||||||
|
hide_timestamps = this.props.hideTimestamp || ! t.chat.context.get('chat.video-chat.timestamps'),
|
||||||
|
|
||||||
bg_css = msg.mentioned && msg.mention_color ? t.site_chat.inverse_colors.process(msg.mention_color) : null;
|
bg_css = msg.mentioned && msg.mention_color ? t.site_chat.inverse_colors.process(msg.mention_color) : null;
|
||||||
|
|
||||||
|
@ -288,7 +299,7 @@ export default class VideoChatHook extends Module {
|
||||||
class={`tw-align-items-start tw-flex tw-flex-nowrap tw-full-width tw-pd-l-05 tw-pd-y-05 vod-message${msg.is_sub ? ' ffz-notice-line ffz--subscribe-line' : ''}${msg.highlight ? ' ffz-notice-line ffz--points-line' : ''}${highlight ? ' ffz--points-highlight ffz-custom-color' : ''}${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`}
|
class={`tw-align-items-start tw-flex tw-flex-nowrap tw-full-width tw-pd-l-05 tw-pd-y-05 vod-message${msg.is_sub ? ' ffz-notice-line ffz--subscribe-line' : ''}${msg.highlight ? ' ffz-notice-line ffz--points-line' : ''}${highlight ? ' ffz--points-highlight ffz-custom-color' : ''}${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`}
|
||||||
style={{backgroundColor: bg_css}}
|
style={{backgroundColor: bg_css}}
|
||||||
>
|
>
|
||||||
{this.props.hideTimestamp || (<div data-test-selector="message-timestamp" class="tw-align-right tw-flex tw-flex-shrink-0 vod-message__header">
|
{hide_timestamps || (<div data-test-selector="message-timestamp" class="tw-align-right tw-flex tw-flex-shrink-0 vod-message__header">
|
||||||
<div class="tw-mg-r-05">
|
<div class="tw-mg-r-05">
|
||||||
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||||
<button class="tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive" onClick={this.onTimestampClickHandler}>
|
<button class="tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive" onClick={this.onTimestampClickHandler}>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue