diff --git a/src/experiments.js b/src/experiments.js index 45bdc8e2..6838e31e 100644 --- a/src/experiments.js +++ b/src/experiments.js @@ -196,6 +196,24 @@ export default class ExperimentManager extends Module { return null; } + getTwitchKeyFromName(name) { + const experiments = this.getTwitchExperiments(); + if ( ! experiments ) + return undefined; + + name = name.toLowerCase(); + for(const key in experiments) + if ( has(experiments, key) ) { + const data = experiments[key]; + if ( data && data.name && data.name.toLowerCase() === name ) + return key; + } + } + + getTwitchAssignmentByName(name) { + return this.getTwitchAssignment(this.getTwitchKeyFromName(name)); + } + _rebuildTwitchKey(key, is_set, new_val) { const core = this.resolve('site').getCore(), exps = core.experiments, diff --git a/src/main.js b/src/main.js index 5c204e7a..ddf24ddf 100644 --- a/src/main.js +++ b/src/main.js @@ -100,7 +100,7 @@ class FrankerFaceZ extends Module { FrankerFaceZ.Logger = Logger; const VER = FrankerFaceZ.version_info = { - major: 4, minor: 0, revision: 0, extra: '-rc12.1', + major: 4, minor: 0, revision: 0, extra: '-rc12.2', commit: __git_commit__, build: __webpack_hash__, toString: () => diff --git a/src/modules/metadata.jsx b/src/modules/metadata.jsx index 54ff4449..6a0fbdb9 100644 --- a/src/modules/metadata.jsx +++ b/src/modules/metadata.jsx @@ -266,17 +266,36 @@ export default class Metadata extends Module { data = await def.setup.call(this, data); // Let's get refresh logic out of the way now. - const refresh = maybe_call(def.refresh, this, data); + let refresh = maybe_call(def.refresh, this, data); + let fade_in = maybe_call(def.fade_in, this, data); + + // Grab the element again in case it changed, somehow. + el = container.querySelector(`.ffz-sidebar-stat[data-key="${key}"]`); + + if ( refresh && typeof refresh !== 'number' ) + refresh = 1000; + + if ( fade_in && typeof fade_in !== 'number' ) + fade_in = 1500; + + if ( fade_in && el && el._ffz_fading ) { + // If we have a fade-in and we're still fading in, make sure to + // update the metadata when that completes, if not sooner. + const remaining = fade_in - (Date.now() - el._ffz_created); + if ( remaining <= 0 ) { + el._ffz_fading = false; + el.classList.remove('ffz--fade-in'); + + } else if ( ! refresh || remaining <= refresh ) + refresh = remaining; + } + if ( refresh ) timers[key] = setTimeout( () => refresh_fn(key), typeof refresh === 'number' ? refresh : 1000 ); - - // Grab the element again in case it changed, somehow. - el = container.querySelector(`.ffz-sidebar-stat[data-key="${key}"]`); - let stat, old_color, sub_el; const label = maybe_call(def.label, this, data); @@ -335,7 +354,7 @@ export default class Metadata extends Module { if ( def.click ) btn.addEventListener('click', e => { - if ( btn.disabled || btn.classList.contains('disabled') || el.disabled || el.classList.contains('disabled') ) + if ( el._ffz_fading || btn.disabled || btn.classList.contains('disabled') || el.disabled || el.classList.contains('disabled') ) return false; def.click.call(this, el._ffz_data, e, () => refresh_fn(key)); @@ -343,7 +362,7 @@ export default class Metadata extends Module { if ( def.popup ) popup.addEventListener('click', () => { - if ( popup.disabled || popup.classList.contains('disabled') || el.disabled || el.classList.contains('disabled') ) + if ( el._ffz_fading || popup.disabled || popup.classList.contains('disabled') || el.disabled || el.classList.contains('disabled') ) return false; if ( el._ffz_popup ) @@ -428,6 +447,14 @@ export default class Metadata extends Module { if ( order != null ) el.style.order = order; + el._ffz_created = Date.now(); + + if ( fade_in ) { + el._ffz_fading = true; + el.classList.add('ffz--fade-in'); + el.style.setProperty('--ffz-fade-duration', `${fade_in/1000}s`); + } + subcontainer.appendChild(el); if ( def.tooltip ) { diff --git a/src/sites/twitch-twilight/modules/channel.js b/src/sites/twitch-twilight/modules/channel.js index 6d32b998..1a5a244b 100644 --- a/src/sites/twitch-twilight/modules/channel.js +++ b/src/sites/twitch-twilight/modules/channel.js @@ -151,6 +151,8 @@ export default class Channel extends Module { state.videoPlayerSource = expected; } + t.settings.updateContext({hosting: !!state.hostMode}); + } else if ( has(state, 'videoPlayerSource') ) { if ( state.videoPlayerSource !== expected && ! t.settings.get('channel.hosting.enable') ) state.videoPlayerSource = expected; @@ -164,6 +166,9 @@ export default class Channel extends Module { if ( has(state, 'videoPlayerSource') ) state.videoPlayerSource = inst.ffzGetChannel(); } + + if ( has(state, 'isHosting') ) + t.settings.updateContext({hosting: state.isHosting}); } } catch(err) { @@ -177,6 +182,8 @@ export default class Channel extends Module { if ( new_style ) { const hosted = inst.ffzExpectedHost = inst.state.hostMode; + this.settings.updateContext({hosting: this.settings.get('channel.hosting.enable') && !!inst.state.hostMode}); + if ( hosted && ! this.settings.get('channel.hosting.enable') ) { inst.ffzOldSetState({ hostMode: null, @@ -185,7 +192,7 @@ export default class Channel extends Module { } } else { - inst.ffzOldGetHostedLogin = inst.getHostedChannelLogin; + inst.ffzOldGetHostedLogin = () => get('props.data.user.hosting.login', inst) || null; inst.getHostedChannelLogin = function() { return t.settings.get('channel.hosting.enable') ? inst.ffzOldGetHostedLogin() : null; @@ -200,15 +207,15 @@ export default class Channel extends Module { // Store the current state and disable the current host if needed. inst.ffzExpectedHost = inst.state.isHosting ? inst.state.videoPlayerSource : null; - if ( ! this.settings.get('channel.hosting.enable') ) + this.settings.updateContext({hosting: this.settings.get('channel.hosting.enable') && inst.state.isHosting}); + if ( ! this.settings.get('channel.hosting.enable') ) { inst.ffzOldHostHandler(null); + } } // Finally, we force an update so that any child components // receive our updated handler. inst.forceUpdate(); - - } @@ -216,7 +223,12 @@ export default class Channel extends Module { if ( val === undefined ) val = this.settings.get('channel.hosting.enable'); + let hosting = val; + for(const inst of this.ChannelPage.instances) { + if ( ! inst.ffzExpectedHost ) + hosting = false; + if ( has(inst.state, 'hostMode') ) { const host = val ? inst.ffzExpectedHost : null, target = host && host.hostedChannel && host.hostedChannel.login || inst.ffzGetChannel(); @@ -229,5 +241,7 @@ export default class Channel extends Module { } else inst.ffzOldHostHandler(val ? inst.ffzExpectedHost : null); } + + this.settings.updateContext({hosting}); } } \ No newline at end of file diff --git a/src/sites/twitch-twilight/modules/channel_bar.js b/src/sites/twitch-twilight/modules/channel_bar.js index 09ae431e..dcbfd661 100644 --- a/src/sites/twitch-twilight/modules/channel_bar.js +++ b/src/sites/twitch-twilight/modules/channel_bar.js @@ -5,7 +5,6 @@ // ============================================================================ import Module from 'utilities/module'; -import {deep_copy} from 'utilities/object'; import CHANNEL_QUERY from './channel_bar_query.gql'; @@ -15,6 +14,7 @@ export default class ChannelBar extends Module { this.should_enable = true; + this.inject('settings'); this.inject('site.fine'); this.inject('site.apollo'); this.inject('metadata'); @@ -42,6 +42,8 @@ export default class ChannelBar extends Module { this.ChannelBar.on('update', this.updateChannelBar, this); this.ChannelBar.ready((cls, instances) => { + this.settings.updateContext({new_channel: true}); + for(const inst of instances) this.updateChannelBar(inst); }); diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index 8540717d..4ee007f7 100644 --- a/src/sites/twitch-twilight/modules/chat/index.js +++ b/src/sites/twitch-twilight/modules/chat/index.js @@ -6,7 +6,7 @@ import {ColorAdjuster} from 'utilities/color'; import {setChildren} from 'utilities/dom'; -import {has, split_chars, shallow_object_equals, deep_copy} from 'utilities/object'; +import {has, split_chars, shallow_object_equals} from 'utilities/object'; import {FFZEvent} from 'utilities/events'; import Module from 'utilities/module'; @@ -100,6 +100,7 @@ const CHAT_TYPES = ((e = {}) => { e[e.BitsCharity = 29] = 'BitsCharity'; e[e.CrateGift = 30] = 'CrateGift'; e[e.RewardGift = 31] = 'RewardGift'; + e[e.SubMysteryGift = 32] = 'SubMysteryGift'; return e; })(); diff --git a/src/sites/twitch-twilight/modules/css_tweaks/index.js b/src/sites/twitch-twilight/modules/css_tweaks/index.js index 5eb75f8f..583cbf63 100644 --- a/src/sites/twitch-twilight/modules/css_tweaks/index.js +++ b/src/sites/twitch-twilight/modules/css_tweaks/index.js @@ -92,16 +92,44 @@ export default class CSSTweaks extends Module { changed: val => this.toggle('portrait', val) }); - this.settings.add('layout.sidenav-width', { - require: ['context.ui.theatreModeEnabled', 'context.ui.sideNavExpanded'], + this.settings.add('layout.portrait-extra-height', { + requires: ['context.new_channel', 'context.hosting', 'context.ui.theatreModeEnabled', 'player.theatre.no-whispers', 'whispers.show', 'layout.minimal-navigation'], process(ctx) { - if ( ctx.get('context.ui.theatreModeEnabled') ) - return '0'; + let height = 0; + if ( ctx.get('context.ui.theatreModeEnabled') ) { + if ( ctx.get('layout.minimal-navigation') ) + height += 1; + + if ( ctx.get('whispers.show') && ! ctx.get('player.theatre.no-whispers') ) + height += 4; + + } else { + height = ctx.get('layout.minimal-navigation') ? 1 : 5; + if ( ctx.get('whispers.show') ) + height += 4; + + height += ctx.get('context.new_channel') ? 1 : 5; + + if ( ctx.get('context.hosting') ) + height += 4; + } + + return `${height}rem`; + }, + + changed: val => this.setVariable('portrait-extra-height', val) + }) + + this.settings.add('layout.portrait-extra-width', { + require: ['layout.side-nav.show', 'context.ui.theatreModeEnabled', 'context.ui.sideNavExpanded'], + process(ctx) { + if ( ! ctx.get('layout.side-nav.show') || ctx.get('context.ui.theatreModeEnabled') ) + return '0px'; return ctx.get('context.ui.sideNavExpanded') ? '24rem' : '5rem' }, - changed: val => this.setVariable('sidenav-width', val) + changed: val => this.setVariable('portrait-extra-width', val) }); @@ -273,7 +301,8 @@ export default class CSSTweaks extends Module { this.toggleHide('whispers', !this.settings.get('whispers.show')); - this.setVariable('sidenav-width', this.settings.get('layout.sidenav-width')) + this.setVariable('portrait-extra-width', this.settings.get('layout.portrait-extra-width')) + this.setVariable('portrait-extra-height', this.settings.get('layout.portrait-extra-height')) } toggleHide(key, val) { diff --git a/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss b/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss index 41f9f5dd..eccc9f46 100644 --- a/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss +++ b/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss @@ -1,7 +1,7 @@ .twilight-root { - --ffz-player-width: calc(100vw - var(--ffz-sidenav-width)); - --ffz-player-height: calc(calc(var(--ffz-player-width) * 0.5625) + 10rem); - --ffz-theater-height: calc(100vw * 0.5625); + --ffz-player-width: calc(100vw - var(--ffz-portrait-extra-width)); + --ffz-player-height: calc(calc(calc(var(--ffz-player-width) * 0.5625) + var(--ffz-portrait-extra-height))); + --ffz-theater-height: calc(calc(100vw * 0.5625) + var(--ffz-portrait-extra-height)); & > .tw-full-height { height: var(--ffz-player-height) !important; @@ -16,7 +16,7 @@ } } - .persistent-player--theatre { + .persistent-player.persistent-player--theatre { top: 0 !important; left: 0 !important; right: 0 !important; @@ -25,6 +25,11 @@ width: 100% !important; } + .whispers--theatre-mode { + bottom: 5rem !important; + right: 0 !important; + } + .right-column { display: unset !important; position: fixed !important; diff --git a/src/sites/twitch-twilight/modules/host_button.js b/src/sites/twitch-twilight/modules/host_button.js index daf7cc02..c50970c3 100644 --- a/src/sites/twitch-twilight/modules/host_button.js +++ b/src/sites/twitch-twilight/modules/host_button.js @@ -55,6 +55,7 @@ export default class HostButton extends Module { this.metadata.definitions.host = { order: 150, button: true, + fade_in: true, disabled: () => this._host_updating || this._host_error, diff --git a/src/sites/twitch-twilight/modules/video_chat/index.jsx b/src/sites/twitch-twilight/modules/video_chat/index.jsx index 1016e3dc..c58350e9 100644 --- a/src/sites/twitch-twilight/modules/video_chat/index.jsx +++ b/src/sites/twitch-twilight/modules/video_chat/index.jsx @@ -4,9 +4,9 @@ // Video Chat Hooks // ============================================================================ -import {has, get} from 'utilities/object'; +import {get} from 'utilities/object'; import {print_duration} from 'utilities/time'; -import {ClickOutside} from 'utilities/dom'; +//import {ClickOutside} from 'utilities/dom'; import {formatBitsConfig} from '../chat'; import Module from 'utilities/module'; @@ -242,7 +242,7 @@ export default class VideoChatHook extends Module { )}