1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-25 20:18:31 +00:00
* Added: Setting to automatically open chat when navigating to an offline channel.
* Added: Support for multi-month gift sub messages.
* Fixed: The FFZ Control Center, when maximized, disappearing beneath chat on channel pages. (Closes #833)
* Fixed: Metadata appearing in theater mode when hovering over the player.
* Fixed: Automatic theater mode enabling theater mode when navigating back to a channel's landing page.
This commit is contained in:
SirStendec 2020-07-02 14:54:46 -04:00
parent 339b6fdfbb
commit b11e4edf2b
10 changed files with 156 additions and 29 deletions

View file

@ -1,7 +1,7 @@
{
"name": "frankerfacez",
"author": "Dan Salvato LLC",
"version": "4.20.2",
"version": "4.20.3",
"description": "FrankerFaceZ is a Twitch enhancement suite.",
"license": "Apache-2.0",
"scripts": {

View file

@ -164,18 +164,27 @@ export default class MainMenu extends Module {
this.on('i18n:update', this.scheduleUpdate, this);
this.dialog.on('show', () => {
if ( this.dialog.maximized )
this.runFix(1);
this.showing = true;
this.opened = true;
this.updateButtonUnseen();
this.emit('show')
});
this.dialog.on('hide', () => {
if ( this.dialog.maximized )
this.runFix(-1);
this.showing = false;
this.emit('hide');
this.destroyDialog();
});
this.dialog.on('resize', () => {
if ( this.dialog.visible )
this.runFix(this.dialog.maximized ? 1 : -1);
if ( this._vue )
this._vue.$children[0].maximized = this.dialog.maximized
});
@ -189,6 +198,12 @@ export default class MainMenu extends Module {
this.off('site.menu_button:clicked', this.dialog.toggleVisible, this.dialog);
}
runFix(amount) {
this.settings.updateContext({
force_chat_fix: (this.settings.get('context.force_chat_fix') || 0) + amount
});
}
requestPage(page) {
const vue = get('_vue.$children.0', this);

View file

@ -21,12 +21,22 @@ export default class Channel extends Module {
this.inject('i18n');
this.inject('settings');
this.inject('site.css_tweaks');
this.inject('site.fine');
this.inject('site.elemental');
this.inject('site.fine');
this.inject('site.router');
this.inject('site.twitch_data');
this.inject('metadata');
this.inject('socket');
this.settings.add('channel.auto-click-chat', {
default: false,
ui: {
path: 'Channel > Behavior >> General',
title: 'Automatically open chat when opening an offline channel page.',
component: 'setting-check-box'
}
});
/*this.SideNav = this.elemental.define(
'side-nav', '.side-bar-contents .side-nav-section:first-child',
null,
@ -61,6 +71,20 @@ export default class Channel extends Module {
this.InfoBar.on('mutate', this.updateBar, this);
this.InfoBar.on('unmount', this.removeBar, this);
this.InfoBar.each(el => this.updateBar(el));
this.router.on(':route', route => {
if ( route?.name === 'user' )
setTimeout(this.maybeClickChat.bind(this), 1000);
}, this);
this.maybeClickChat();
}
maybeClickChat() {
if ( this.settings.get('channel.auto-click-chat') && this.router.current_name === 'user' ) {
const el = document.querySelector('a[data-a-target="channel-home-tab-Chat"]');
if ( el )
el.click();
}
}
/*updateHidden(el) { // eslint-disable-line class-methods-use-this

View file

@ -5,7 +5,7 @@
// ============================================================================
import {has, get, once, maybe_call, set_equals} from 'utilities/object';
import {TWITCH_GLOBAL_SETS, EmoteTypes, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS, WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS} from 'utilities/constants';
import {TWITCH_GLOBAL_SETS, EmoteTypes, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS, WEBKIT_CSS as WEBKIT, IS_OSX, KNOWN_CODES, TWITCH_EMOTE_BASE, REPLACEMENT_BASE, REPLACEMENTS, KEYS} from 'utilities/constants';
import {ClickOutside} from 'utilities/dom';
import Twilight from 'site';
@ -506,11 +506,14 @@ export default class EmoteMenu extends Module {
hidden = storage.get('emote-menu.hidden-sets');
this.state = {
active: false,
activeEmote: -1,
hidden: hidden && props.data && hidden.includes(props.data.hide_key || props.data.key),
collapsed: collapsed && props.data && collapsed.includes(props.data.key),
intersecting: window.IntersectionObserver ? false : true
}
this.keyHeading = this.keyHeading.bind(this);
this.clickHeading = this.clickHeading.bind(this);
this.clickEmote = this.clickEmote.bind(this);
@ -521,15 +524,23 @@ export default class EmoteMenu extends Module {
}
componentDidMount() {
this.props.addSection(this);
if ( this.ref )
this.props.startObserving(this.ref, this);
}
componentWillUnmount() {
this.props.removeSection(this);
if ( this.ref )
this.props.stopObserving(this.ref);
}
keyInteract(code) {
}
clickEmote(event) {
if ( this.props.visibility_control ) {
const ds = event.currentTarget.dataset;
@ -560,6 +571,11 @@ export default class EmoteMenu extends Module {
this.props.onClickToken(event.currentTarget.dataset.name)
}
keyHeading(event) {
if ( event.keyCode === KEYS.Enter || event.keyCode === KEYS.Space )
this.clickHeading();
}
clickHeading() {
if ( this.props.visibility_control ) {
const hidden = storage.get('emote-menu.hidden-sets') || [],
@ -672,7 +688,7 @@ export default class EmoteMenu extends Module {
source = 'FrankerFaceZ';
return (<section ref={this.saveRef} data-key={data.key} class={filtered ? 'filtered' : ''} onMouseEnter={this.mouseEnter}>
{show_heading ? (<heading class="tw-pd-1 tw-border-b tw-flex tw-flex-nowrap" onClick={this.clickHeading}>
{show_heading ? (<heading tabindex="0" class="tw-pd-1 tw-border-b tw-flex tw-flex-nowrap" onKeyDown={this.keyHeading} onClick={this.clickHeading}>
{image}
<div class="tw-pd-l-05">
{(data.i18n ? t.i18n.t(data.i18n, data.title) : data.title) || t.i18n.t('emote-menu.unknown', 'Unknown Source')}
@ -928,6 +944,9 @@ export default class EmoteMenu extends Module {
this.createObserver();
}
this.sections = [];
this.activeSection = -1;
this.state = {
tab: null,
tone: t.settings.provider.get('emoji-tone', null)
@ -940,6 +959,22 @@ export default class EmoteMenu extends Module {
this.observing = new Map;
this.addSection = inst => {
if ( ! this.sections.includes(inst) )
this.sections.push(inst);
}
this.removeSection = inst => {
const idx = this.sections.indexOf(inst);
if ( idx !== -1 ) {
this.sections.splice(idx);
if ( idx === this.activeSection )
this.activeSection = -1;
else if ( idx < this.activeSection )
this.activeSection--;
}
}
this.startObserving = this.startObserving.bind(this);
this.stopObserving = this.stopObserving.bind(this);
this.handleObserve = this.handleObserve.bind(this);
@ -1161,8 +1196,13 @@ export default class EmoteMenu extends Module {
}
handleKeyDown(event) {
if ( event.keyCode === 27 )
const code = event.keyCode;
if ( code === KEYS.Escape )
this.props.toggleVisibility();
else
return;
event.preventDefault();
}
loadData(force = false, props, state) {
@ -2042,6 +2082,8 @@ export default class EmoteMenu extends Module {
filtered: this.state.filtered,
visibility_control: visibility,
onClickToken: this.props.onClickToken,
addSection: this.addSection,
removeSection: this.removeSection,
startObserving: this.startObserving,
stopObserving: this.stopObserving
}

View file

@ -1818,6 +1818,7 @@ export default class ChatHook extends Module {
login: e.recipientLogin,
displayName: e.recipientName
};
out.sub_months = e.giftMonths;
out.sub_plan = e.methods;
out.sub_total = e.senderCount;
@ -1865,6 +1866,7 @@ export default class ChatHook extends Module {
login: e.recipientLogin,
displayName: e.recipientName
};
out.sub_months = e.giftMonths;
out.sub_plan = e.methods;
out.sub_total = e.senderCount;

View file

@ -462,9 +462,13 @@ other {# messages were deleted by a moderator.}
} else if ( msg.ffz_type === 'sub_gift' ) {
const plan = msg.sub_plan || {},
months = msg.sub_months || 1,
tier = SUB_TIERS[plan.plan] || 1;
const sub_msg = t.i18n.tList('chat.sub.mystery', '{user} gifted a {plan} Sub to {recipient}! ', {
let sub_msg;
const bits = {
months,
user: (msg.sub_anon || user.username === 'ananonymousgifter') ?
t.i18n.t('chat.sub.anonymous-gifter', 'An anonymous gifter') :
e('span', {
@ -484,7 +488,13 @@ other {# messages were deleted by a moderator.}
}, e('span', {
className: 'tw-c-text-base tw-strong'
}, msg.sub_recipient.displayName))
});
};
if ( months <= 1 )
sub_msg = t.i18n.tList('chat.sub.mystery', '{user} gifted a {plan} Sub to {recipient}! ', bits);
else
sub_msg = t.i18n.tList('chat.sub.gift-months', '{user} gifted {months,number} month{months,en_plural} of {plan} Sub to {recipient}!', bits);
if ( msg.sub_total === 1 )
sub_msg.push(t.i18n.t('chat.sub.gift-first', "It's their first time gifting a Sub in the channel!"));

View file

@ -75,11 +75,20 @@ export default class CSSTweaks extends Module {
});
this.settings.add('layout.use-chat-fix', {
requires: ['layout.swap-sidebars', 'layout.use-portrait', 'chat.use-width'],
requires: ['context.force_chat_fix', 'layout.swap-sidebars', 'layout.use-portrait', 'chat.use-width'],
process(ctx) {
return ctx.get('layout.swap-sidebars') || ctx.get('layout.use-portrait') || ctx.get('chat.use-width')
return ctx.get('context.force_chat_fix') || ctx.get('layout.swap-sidebars') || ctx.get('layout.use-portrait') || ctx.get('chat.use-width')
},
changed: val => {
if ( val )
this.toggle('chat-no-animate', true);
else if ( ! val && ! this._no_anim_timer )
this._no_anim_timer = requestAnimationFrame(() => {
this._no_anim_timer = null;
if ( ! this.settings.get('layout.use-chat-fix') )
this.toggle('chat-no-animate', false);
});
this.toggle('chat-fix', val);
this.emit('site.player:fix-player');
}

View file

@ -1,12 +1,17 @@
.channel-root__scroll-area--theatre-mode .channel-info-bar {
.channel-root__scroll-area--theatre-mode {
.channel-info-content > div:first-child,
.channel-info-bar {
position: fixed;
bottom: 10rem;
left: 5rem;
right: calc(var(--ffz-chat-width) + 25rem);
right: calc(var(--ffz-chat-width) + 40rem);
z-index: 3500;
opacity: 0;
width: unset !important;
border: 1px solid var(--color-background-alt);
border-radius: 1rem;
pointer-events: none;
.tw-tooltip-wrapper, .tw-stat, .ffz-stat, button, a {
@ -14,7 +19,11 @@
}
}
.channel-root__scroll-area--theatre-mode:hover .channel-info-bar {
&:hover {
.channel-info-content > div:first-child,
.channel-info-bar {
background-color: var(--color-background-base);
opacity: 0.75;
}
}
}

View file

@ -1495,11 +1495,19 @@ export default class Player extends Module {
tryTheatreMode(inst) {
if ( ! this.settings.get('player.theatre.auto-enter') )
if ( ! inst._ffz_theater_timer )
inst._ffz_theater_timer = setTimeout(() => {
inst._ffz_theater_timer = null;
if ( ! this.settings.get('player.theatre.auto-enter') || ! inst._ffz_mounted )
return;
if ( inst.props.channelHomeLive || inst.props.channelHomeCarousel )
return;
if ( inst?.props?.onTheatreModeEnabled )
inst.props.onTheatreModeEnabled();
}, 250);
}

View file

@ -19,9 +19,17 @@ export const LV_SOCKET_SERVER = 'wss://cbenni.com/socket.io/';
export const KEYS = {
Space: 32,
Enter: 13,
Escape: 27,
Space: 32,
PageUp: 33,
PageDown: 34,
End: 35,
Home: 36,
ArrowLeft: 37,
ArrowUp: 38,
ArrowRight: 39,
ArrowDown: 40
};