diff --git a/src/main.js b/src/main.js index 71d92b2e..a03a6cde 100644 --- a/src/main.js +++ b/src/main.js @@ -151,7 +151,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}` FrankerFaceZ.Logger = Logger; const VER = FrankerFaceZ.version_info = { - major: 4, minor: 5, revision: 0, + major: 4, minor: 5, revision: 1, commit: __git_commit__, build: __webpack_hash__, toString: () => diff --git a/src/sites/twitch-twilight/modules/channel.js b/src/sites/twitch-twilight/modules/channel.js index ff363af3..e7b36a4d 100644 --- a/src/sites/twitch-twilight/modules/channel.js +++ b/src/sites/twitch-twilight/modules/channel.js @@ -41,6 +41,15 @@ export default class Channel extends Module { } }); + this.settings.add('channel.squads.no-autojoin', { + default: false, + ui: { + path: 'Channel > Behavior >> Squads', + title: 'Do not automatically redirect to Squad Streams.', + component: 'setting-check-box' + } + }); + this.ChannelPage = this.fine.define( 'channel-page', @@ -53,6 +62,12 @@ export default class Channel extends Module { n => n.handleLeaveRaid && n.handleJoinRaid, Twilight.CHAT_ROUTES ); + + this.SquadController = this.fine.define( + 'squad-controller', + n => n.onSquadPage && n.isValidSquad && n.handleLeaveSquad, + Twilight.CHAT_ROUTES + ); } @@ -61,6 +76,9 @@ export default class Channel extends Module { this.RaidController.on('mount', this.wrapRaidController, this); this.RaidController.on('update', this.noAutoRaids, this); + this.SquadController.on('mount', this.noAutoSquads, this); + this.SquadController.on('update', this.noAutoSquads, this); + this.RaidController.ready((cls, instances) => { for(const inst of instances) this.wrapRaidController(inst); @@ -128,6 +146,17 @@ export default class Channel extends Module { } + noAutoSquads(inst) { + if ( this.settings.get('channel.squads.no-autojoin') ) + setTimeout(() => { + if ( inst.isValidSquad() && inst.state && inst.state.hasJoined ) { + this.log.info('Automatically opting out of Squad Stream.'); + inst.handleLeaveSquad(); + } + }); + } + + noAutoRaids(inst) { if ( this.settings.get('channel.raids.no-autojoin') ) setTimeout(() => { diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index 334a132c..579b9e3e 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, make_enum, split_chars, shallow_object_equals, set_equals} from 'utilities/object'; +import {get, has, make_enum, split_chars, shallow_object_equals, set_equals} from 'utilities/object'; import {FFZEvent} from 'utilities/events'; import Module from 'utilities/module'; @@ -197,9 +197,29 @@ export default class ChatHook extends Module { Twilight.CHAT_ROUTES ); + this.InlineCallout = this.fine.define( + 'inline-callout', + n => n.showCTA && n.toggleContextMenu && n.actionClick, + Twilight.CHAT_ROUTES + ); + + this.PinnedCallout = this.fine.define( + 'pinned-callout', + n => n.getCalloutTitle && n.buildCalloutProps && n.pin, + Twilight.CHAT_ROUTES + ); // Settings + this.settings.add('chat.pin-resubs', { + default: false, + ui: { + path: 'Chat > Behavior >> General', + title: 'Automatically pin re-subscription messages in chat.', + component: 'setting-check-box' + } + }); + this.settings.add('chat.width', { default: 340, ui: { @@ -483,6 +503,12 @@ export default class ChatHook extends Module { this.chat.context.on('changed:chat.filtering.display-deleted', this.updateChatLines, this); this.chat.context.on('changed:chat.filtering.display-mod-action', this.updateChatLines, this); this.chat.context.on('changed:chat.filtering.clickable-mentions', val => this.css_tweaks.toggle('clickable-mentions', val)); + this.chat.context.on('changed:chat.pin-resubs', val => { + if ( val ) { + this.updateInlineCallouts(); + this.updatePinnedCallouts(); + } + }, this); this.chat.context.on('changed:chat.lines.alternate', val => { this.css_tweaks.toggle('chat-rows', val); @@ -518,6 +544,14 @@ export default class ChatHook extends Module { this.updateLineBorders(); this.updateMentionCSS(); + this.InlineCallout.on('mount', this.onInlineCallout, this); + this.InlineCallout.on('update', this.onInlineCallout, this); + this.InlineCallout.ready(() => this.updateInlineCallouts()); + + this.PinnedCallout.on('mount', this.onPinnedCallout, this); + this.PinnedCallout.on('update', this.onPinnedCallout, this); + this.PinnedCallout.ready(() => this.updatePinnedCallouts()); + this.ChatController.on('mount', this.chatMounted, this); this.ChatController.on('unmount', this.chatUnmounted, this); this.ChatController.on('receive-props', this.chatUpdated, this); @@ -661,6 +695,52 @@ export default class ChatHook extends Module { } + updatePinnedCallouts() { + for(const inst of this.PinnedCallout.instances) + this.onPinnedCallout(inst); + } + + onPinnedCallout(inst) { + if ( ! this.chat.context.get('chat.pin-resubs') || inst._ffz_pinned ) + return; + + const props = inst.props, + event = props && props.event; + if ( props.pinned || ! event || event.type !== 'share-resub' ) + return; + + this.log.info('Automatically pinning re-sub notice.'); + inst._ffz_pinned = true; + inst.pin(); + } + + updateInlineCallouts() { + for(const inst of this.InlineCallout.instances) + this.onInlineCallout(inst); + } + + onInlineCallout(inst) { + if ( ! this.chat.context.get('chat.pin-resubs') || inst._ffz_pinned ) + return; + + const event = get('props.event.callout', inst); + if ( ! event || event.cta !== 'Share' ) + return; + + const onPin = get('contextMenuProps.onPin', event); + if ( ! onPin ) + return; + + this.log.info('Automatically pinning re-sub notice.'); + inst._ffz_pinned = true; + + if ( inst.hideOnContextMenuAction ) + inst.hideOnContextMenuAction(onPin)(); + else + onPin(); + } + + tryUpdateBadges() { if ( !this._badge_timer ) this._badge_timer = setTimeout(() => this._tryUpdateBadges(), 0);