diff --git a/package.json b/package.json index 89cb7bea..bf86502f 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.19.5", + "version": "4.19.6", "description": "FrankerFaceZ is a Twitch enhancement suite.", "license": "Apache-2.0", "scripts": { diff --git a/src/experiments.json b/src/experiments.json index 51ebd3b1..c65bfa4d 100644 --- a/src/experiments.json +++ b/src/experiments.json @@ -3,8 +3,8 @@ "name": "New API Stress Testing", "description": "Send duplicate requests to the new API server for load testing.", "groups": [ - {"value": true, "weight": 75}, - {"value": false, "weight": 25} + {"value": true, "weight": 50}, + {"value": false, "weight": 50} ] } } \ No newline at end of file diff --git a/src/modules/chat/emotes.js b/src/modules/chat/emotes.js index b4668e0c..50ad2068 100644 --- a/src/modules/chat/emotes.js +++ b/src/modules/chat/emotes.js @@ -963,6 +963,9 @@ function determineEmoteType(emote) { if ( emote.setID == 300238151 ) return EmoteTypes.ChannelPoints; + if ( emote.setID == 300374282 ) + return EmoteTypes.TwoFactor; + const id = parseInt(emote.setID, 10); if ( ! isNaN(id) && isFinite(id) && id >= 5e8 ) return EmoteTypes.BitsTier; @@ -983,6 +986,9 @@ function determineSetType(set) { if ( TWITCH_PRIME_SETS.includes(id) ) return EmoteTypes.Prime; + if ( id == 300374282 ) + return EmoteTypes.TwoFactor; + const owner = set.owner; if ( owner ) { if ( owner.id == 139075904 ) diff --git a/src/modules/chat/tokenizers.jsx b/src/modules/chat/tokenizers.jsx index b6cd49bb..40792a69 100644 --- a/src/modules/chat/tokenizers.jsx +++ b/src/modules/chat/tokenizers.jsx @@ -1059,6 +1059,9 @@ export const AddonEmotes = { } else if ( type === EmoteTypes.Prime || type === EmoteTypes.Turbo ) source = this.i18n.t('emote.prime', 'Twitch Prime'); + else if ( type === EmoteTypes.TwoFactor ) + source = this.i18n.t('emote.2fa', 'Twitch 2FA Emote'); + else if ( type === EmoteTypes.LimitedTime ) source = this.i18n.t('emote.limited', 'Limited-Time Only Emote'); diff --git a/src/modules/main_menu/components/setting-color-box.vue b/src/modules/main_menu/components/setting-color-box.vue index f949d664..14eee276 100644 --- a/src/modules/main_menu/components/setting-color-box.vue +++ b/src/modules/main_menu/components/setting-color-box.vue @@ -13,6 +13,7 @@ :id="item.full_key" ref="control" :alpha="alpha" + :open-up="openUp" :nullable="true" :value="color" @input="onInput" @@ -73,6 +74,13 @@ export default { return this.value; }, + openUp() { + if ( this.item.openUp != null ) + return this.item.openUp; + + return false; + }, + alpha() { if ( this.item.alpha != null ) return this.item.alpha; diff --git a/src/sites/twitch-twilight/modules/channel.js b/src/sites/twitch-twilight/modules/channel.js index 8b160963..6f5e97ef 100644 --- a/src/sites/twitch-twilight/modules/channel.js +++ b/src/sites/twitch-twilight/modules/channel.js @@ -56,7 +56,7 @@ export default class Channel extends Module { this.ChannelPage = this.fine.define( 'channel-page', n => (n.updateHost && n.updateChannel && n.state && has(n.state, 'hostedChannel')) || (n.getHostedChannelLogin && n.handleHostingChange) || (n.onChatHostingChange && n.state && has(n.state, 'hostMode')), - ['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following'] + ['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following', 'mod-view'] ); this.RaidController = this.fine.define( diff --git a/src/sites/twitch-twilight/modules/channel_bar.jsx b/src/sites/twitch-twilight/modules/channel_bar.jsx index 575d2b27..85ce2ef4 100644 --- a/src/sites/twitch-twilight/modules/channel_bar.jsx +++ b/src/sites/twitch-twilight/modules/channel_bar.jsx @@ -6,6 +6,7 @@ import Module from 'utilities/module'; import {get} from 'utilities/object'; +import {createElement} from 'utilities/dom'; //import CHANNEL_QUERY from './channel_header_query.gql'; @@ -57,6 +58,12 @@ export default class ChannelBar extends Module { n => n.getTitle && n.getGame && n.renderGame, ['user'] ); + + this.ModWidget = this.fine.define( + 'mod-widget', + n => n.renderToolbar && n.getToolbarControls && n.childContext, + ['mod-view'] + ); } onEnable() { @@ -77,6 +84,15 @@ export default class ChannelBar extends Module { }); + /*this.ModWidget.on('mount', this.updateModWidget, this); + this.ModWidget.on('update', this.updateModWidget, this); + + this.ModWidget.ready((cls, instances) => { + for(const inst of instances) + this.updateModWidget(inst); + });*/ + + //this.VideoBar.on('unmount', this.unmountVideoBar, this); this.VideoBar.on('mount', this.updateVideoBar, this); this.VideoBar.on('update', this.updateVideoBar, this); @@ -89,6 +105,30 @@ export default class ChannelBar extends Module { } + /*updateModWidget(inst) { + const container = this.fine.getChildNode(inst); + if ( ! container || ! container.querySelector('.video-player-hosting-ui__container') ) + return; + + const header = container.querySelector('.mod-view-panel-header'); + if ( ! header ) + return; + + let cont = header.querySelector('.ffz--stat-container'); + + if ( ! cont ) { + cont =
; + const contcont = header.querySelector(':scope > div:first-child > div'); + if ( ! contcont ) + return; + + contcont.appendChild(cont); + } + + this.log.info('mod-widget', inst, cont); + }*/ + + updateVideoBar(inst) { const container = this.fine.getChildNode(inst), timestamp = container && container.querySelector('[data-test-selector="date"]'); diff --git a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx index 68ae1eea..d6d174b5 100644 --- a/src/sites/twitch-twilight/modules/chat/emote_menu.jsx +++ b/src/sites/twitch-twilight/modules/chat/emote_menu.jsx @@ -637,11 +637,17 @@ export default class EmoteMenu extends Module { return; const locked = emote.locked && (! lock || ! lock.emotes.has(emote.id)), - emote_lock = locked && data.locks && data.locks[emote.set_id], - sellout = emote_lock ? (data.all_locked ? - t.i18n.t('emote-menu.emote-sub', 'Subscribe for {price} to unlock this emote.', emote_lock) : - t.i18n.t('emote-menu.emote-up', 'Upgrade your sub to {price} to unlock this emote.', emote_lock) - ) : ''; + emote_lock = locked && data.locks && data.locks[emote.set_id]; + let sellout = ''; + + if ( emote_lock ) { + if ( emote_lock.id === 'cheer' ) { + sellout = t.i18n.t('emote-menu.emote-cheer', 'Cheer an additional {bits_remaining,number} bit{bits_remaining,en_plural} to unlock this emote.', emote_lock); + } else if ( data.all_locked ) + sellout = t.i18n.t('emote-menu.emote-sub', 'Subscribe for {price} to unlock this emote.', emote_lock); + else + sellout = t.i18n.t('emote-menu.emote-up', 'Upgrade your sub to {price} to unlock this emote.', emote_lock); + } return this.renderEmote( emote, @@ -698,14 +704,18 @@ export default class EmoteMenu extends Module { if ( ! data.all_locked || ! data.locks ) return null; - const lock = data.locks[this.state.unlocked]; + const lock = data.locks[this.state.unlocked], + locks = Object.values(data.locks).filter(x => x.id !== 'cheer'); + + if ( ! locks.length ) + return null; return (