2017-11-13 01:23:39 -05:00
'use strict' ;
// ============================================================================
// Chat Hooks
// ============================================================================
2018-05-10 19:56:39 -04:00
import { ColorAdjuster } from 'utilities/color' ;
2020-06-30 19:48:46 -04:00
import { get , has , make _enum , shallow _object _equals , set _equals , deep _equals } from 'utilities/object' ;
2019-11-11 14:38:49 -05:00
import { WEBKIT _CSS as WEBKIT } from 'utilities/constants' ;
2018-05-22 17:23:20 -04:00
import { FFZEvent } from 'utilities/events' ;
2017-11-13 01:23:39 -05:00
import Module from 'utilities/module' ;
2018-03-14 13:58:04 -04:00
import Twilight from 'site' ;
2017-11-14 04:12:10 -05:00
import Scroller from './scroller' ;
2017-11-14 22:13:30 -05:00
import ChatLine from './line' ;
2017-11-15 14:04:59 -05:00
import SettingsMenu from './settings_menu' ;
2018-04-06 21:12:12 -04:00
import EmoteMenu from './emote_menu' ;
2019-05-07 15:04:12 -04:00
import Input from './input' ;
2019-05-31 23:21:49 -04:00
import ViewerCards from './viewer_card' ;
2017-11-14 22:13:30 -05:00
2020-06-30 19:48:46 -04:00
/ * c o n s t R E G E X _ E M O T E S = {
2018-06-27 20:17:45 -04:00
'B-?\\)' : [ 'B)' , 'B-)' ] ,
'R-?\\)' : [ 'R)' , 'R-)' ] ,
'[oO](_|\\.)[oO]' : [ 'o_o' , 'O_o' , 'o_O' , 'O_O' , 'o.o' , 'O.o' , 'o.O' , 'O.O' ] ,
'\\>\\;\\(' : [ '>(' ] ,
'\\<\\;3' : [ '<3' ] ,
'\\:-?(o|O)' : [ ':o' , ':O' , ':-o' , ':-O' ] ,
'\\:-?(p|P)' : [ ':p' , ':P' , ':-p' , ':-P' ] ,
'\\:-?D' : [ ':D' , ':-D' ] ,
'\\:-?[\\\\/]' : [ ':/' , ':-/' , ':\\' , ':-\\' ] ,
'\\:-?[z|Z|\\|]' : [ ':z' , ':Z' , ':|' , ':-z' , ':-Z' , ':-|' ] ,
'\\:-?\\(' : [ ':(' , ':-(' ] ,
'\\:-?\\)' : [ ':)' , ':-)' ] ,
'\\;-?(p|P)' : [ ';p' , ';P' , ';-p' , ';-P' ] ,
2018-06-29 19:45:33 -04:00
'\\;-?\\)' : [ ';)' , ';-)' ] ,
'#-?[\\\\/]' : [ '#/' , '#-/' , '#//' , '#-//' ] ,
':-?(?:7|L)' : [ ':7' , ':L' , ':-7' , ':-L' ] ,
'\\<\\;\\]' : [ '<]' ] ,
'\\:-?(S|s)' : [ ':s' , ':S' , ':-s' , ':-S' ] ,
'\\:\\>\\;' : [ ':>' ]
2020-06-30 19:48:46 -04:00
} ; * /
2018-06-27 20:17:45 -04:00
2018-10-16 18:04:54 -04:00
const MESSAGE _TYPES = make _enum (
'Post' ,
'Action' ,
'PostWithMention'
) ;
const MOD _TYPES = make _enum (
'Ban' ,
'Timeout' ,
'Delete'
) ;
const AUTOMOD _TYPES = make _enum (
'MessageRejectedPrompt' ,
'CheerMessageRejectedPrompt' ,
'MessageRejected' ,
'MessageAllowed' ,
'MessageDenied' ,
'CheerMessageDenied' ,
'CheerMessageTimeout' ,
'MessageModApproved' ,
'MessageModDenied'
) ;
const CHAT _TYPES = make _enum (
'Message' ,
'ExtensionMessage' ,
'Moderation' ,
'ModerationAction' ,
'TargetedModerationAction' ,
'AutoMod' ,
'SubscriberOnlyMode' ,
'FollowerOnlyMode' ,
'SlowMode' ,
'EmoteOnlyMode' ,
'R9KMode' ,
'Connected' ,
'Disconnected' ,
'Reconnect' ,
'Hosting' ,
'Unhost' ,
'Hosted' ,
'Subscription' ,
'Resubscription' ,
'GiftPaidUpgrade' ,
2018-11-12 13:34:53 -05:00
'AnonGiftPaidUpgrade' ,
2019-03-14 21:43:44 -04:00
'PrimePaidUpgrade' ,
2019-09-13 17:03:50 -04:00
'PrimeCommunityGiftReceivedEvent' ,
2019-08-09 14:24:26 -04:00
'ExtendSubscription' ,
2018-10-16 18:04:54 -04:00
'SubGift' ,
2018-11-12 13:34:53 -05:00
'AnonSubGift' ,
2018-10-16 18:04:54 -04:00
'Clear' ,
'RoomMods' ,
'RoomState' ,
'Raid' ,
'Unraid' ,
'Ritual' ,
'Notice' ,
'Info' ,
'BadgesUpdated' ,
'Purchase' ,
'BitsCharity' ,
'CrateGift' ,
'RewardGift' ,
2018-11-12 13:34:53 -05:00
'SubMysteryGift' ,
2018-12-03 18:08:32 -05:00
'AnonSubMysteryGift' ,
2019-10-22 18:32:56 -04:00
'StandardPayForward' ,
'CommunityPayForward' ,
2019-02-05 14:24:45 -05:00
'FirstCheerMessage' ,
2019-04-18 03:16:19 -04:00
'BitsBadgeTierMessage' ,
2019-06-14 21:24:48 -04:00
'InlinePrivateCallout' ,
'ChannelPointsReward'
2018-10-16 18:04:54 -04:00
) ;
2017-11-14 22:13:30 -05:00
const NULL _TYPES = [
'Reconnect' ,
'RoomState' ,
2018-04-28 17:56:03 -04:00
'BadgesUpdated' ,
'Clear'
2017-11-14 22:13:30 -05:00
] ;
2017-11-14 04:12:10 -05:00
2018-04-28 17:56:03 -04:00
const MISBEHAVING _EVENTS = [
2018-02-22 18:23:44 -05:00
'onBadgesUpdatedEvent' ,
2017-11-13 01:23:39 -05:00
] ;
export default class ChatHook extends Module {
constructor ( ... args ) {
super ( ... args ) ;
this . should _enable = true ;
this . colors = new ColorAdjuster ;
2018-04-28 17:56:03 -04:00
this . inverse _colors = new ColorAdjuster ;
2017-11-13 01:23:39 -05:00
this . inject ( 'settings' ) ;
2018-07-16 18:02:08 -04:00
this . inject ( 'i18n' ) ;
2017-11-13 01:23:39 -05:00
this . inject ( 'site' ) ;
this . inject ( 'site.router' ) ;
this . inject ( 'site.fine' ) ;
this . inject ( 'site.web_munch' ) ;
this . inject ( 'site.css_tweaks' ) ;
this . inject ( 'chat' ) ;
2017-11-14 04:12:10 -05:00
this . inject ( Scroller ) ;
2017-11-14 22:13:30 -05:00
this . inject ( ChatLine ) ;
2017-11-15 14:04:59 -05:00
this . inject ( SettingsMenu ) ;
2018-04-06 21:12:12 -04:00
this . inject ( EmoteMenu ) ;
2019-05-07 15:04:12 -04:00
this . inject ( Input ) ;
2019-05-31 23:21:49 -04:00
this . inject ( ViewerCards ) ;
2018-05-10 19:56:39 -04:00
2018-08-28 19:13:26 -04:00
this . ChatService = this . fine . define (
'chat-service' ,
2018-09-11 19:08:58 -04:00
n => n . join && n . connectHandlers ,
2018-08-28 19:13:26 -04:00
Twilight . CHAT _ROUTES
) ;
this . ChatBuffer = this . fine . define (
'chat-buffer' ,
n => n . updateHandlers && n . delayedMessageBuffer && n . handleMessage ,
Twilight . CHAT _ROUTES
) ;
2017-11-13 01:23:39 -05:00
this . ChatController = this . fine . define (
'chat-controller' ,
2018-08-28 19:13:26 -04:00
n => n . hostingHandler && n . onRoomStateUpdated ,
2018-03-14 13:58:04 -04:00
Twilight . CHAT _ROUTES
2017-11-13 01:23:39 -05:00
) ;
this . ChatContainer = this . fine . define (
'chat-container' ,
2020-01-22 16:58:55 -05:00
n => n . closeViewersList && n . onChatInputFocus ,
2018-03-14 13:58:04 -04:00
Twilight . CHAT _ROUTES
2017-11-13 01:23:39 -05:00
) ;
2018-08-28 19:13:26 -04:00
this . ChatBufferConnector = this . fine . define (
'chat-buffer-connector' ,
n => n . clearBufferHandle && n . syncBufferedMessages ,
Twilight . CHAT _ROUTES
) ;
2020-06-30 19:48:46 -04:00
this . joined _raids = new Set ;
this . RaidController = this . fine . define (
'raid-controller' ,
n => n . handleLeaveRaid && n . handleJoinRaid ,
2018-03-14 13:58:04 -04:00
Twilight . CHAT _ROUTES
2020-06-30 19:48:46 -04:00
) ;
2018-04-28 17:56:03 -04:00
2019-06-18 19:48:51 -04:00
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
) ;
2017-11-13 01:23:39 -05:00
2019-09-13 17:03:50 -04:00
this . CalloutSelector = this . fine . define (
'callout-selector' ,
n => n . selectCalloutComponent && n . props && n . props . callouts ,
Twilight . CHAT _ROUTES
) ;
this . PointsButton = this . fine . define (
'points-button' ,
n => n . renderIcon && n . renderFlame && n . handleIconAnimationComplete ,
Twilight . CHAT _ROUTES
) ;
this . PointsClaimButton = this . fine . define (
'points-claim-button' ,
n => n . getClaim && n . onClick && n . props && n . props . claimCommunityPoints ,
Twilight . CHAT _ROUTES
) ;
2019-09-23 12:17:51 -04:00
this . CommunityChestBanner = this . fine . define (
'community-chest-banner' ,
n => n . getLastGifterText && n . getBannerText && has ( n , 'finalCount' ) ,
Twilight . CHAT _ROUTES
) ;
2019-11-11 14:38:49 -05:00
this . PointsInfo = this . fine . define (
'points-info' ,
n => n . pointIcon !== undefined && n . pointName !== undefined ,
Twilight . CHAT _ROUTES
) ;
this . GiftBanner = this . fine . define (
'gift-banner' ,
2020-01-11 17:13:56 -05:00
n => n . getBannerText && n . onGiftMoreClick ,
2019-11-11 14:38:49 -05:00
Twilight . CHAT _ROUTES
) ;
2017-11-13 01:23:39 -05:00
// Settings
2020-06-30 19:48:46 -04:00
this . settings . add ( 'channel.raids.no-autojoin' , {
default : false ,
ui : {
path : 'Channel > Behavior >> Raids' ,
title : 'Do not automatically join raids.' ,
component : 'setting-check-box'
}
} ) ;
2019-12-10 20:46:33 -05:00
this . settings . add ( 'chat.hide-community-highlights' , {
default : false ,
ui : {
path : 'Chat > Appearance >> Community' ,
title : 'Hide all Community Highlights from the top of chat.' ,
component : 'setting-check-box' ,
description : 'Community Highlights are polls, community gift subs, etc. that float over the top of chat temporarily with no way to close them.'
}
} ) ;
2019-11-11 14:38:49 -05:00
this . settings . add ( 'chat.subs.gift-banner' , {
default : true ,
ui : {
2019-12-10 20:46:33 -05:00
path : 'Chat > Appearance >> Community' ,
2019-11-11 14:38:49 -05:00
title : 'Display a banner at the top of chat when a mass gift sub happens.' ,
component : 'setting-check-box'
}
} ) ;
2020-03-06 01:44:33 -05:00
this . settings . add ( 'chat.banners.hype-train' , {
default : true ,
ui : {
path : 'Chat > Appearance >> Community' ,
title : 'Allow the Hype Train to be displayed in chat.' ,
component : 'setting-check-box' ,
}
} ) ;
2020-03-06 23:29:25 -05:00
this . settings . add ( 'chat.banners.polls' , {
default : true ,
ui : {
path : 'Chat > Appearance >> Community' ,
title : 'Allow Polls to be displayed in chat.' ,
component : 'setting-check-box'
}
} ) ;
2019-09-23 12:17:51 -04:00
this . settings . add ( 'chat.community-chest.show' , {
default : true ,
ui : {
2019-12-10 20:46:33 -05:00
path : 'Chat > Appearance >> Community' ,
2019-09-23 12:17:51 -04:00
title : 'Display the Community Gift Chest banner.' ,
component : 'setting-check-box'
}
} ) ;
2020-01-22 16:58:55 -05:00
this . settings . add ( 'chat.points.allow-highlight' , {
2019-11-11 14:38:49 -05:00
default : true ,
ui : {
path : 'Chat > Channel Points >> Appearance' ,
2020-01-22 16:58:55 -05:00
title : 'Highlight the message in chat when someone redeems Highlight My Message.' ,
2019-11-11 14:38:49 -05:00
component : 'setting-check-box'
}
} ) ;
2019-09-13 17:03:50 -04:00
this . settings . add ( 'chat.points.show-callouts' , {
default : true ,
ui : {
path : 'Chat > Channel Points >> General' ,
title : 'Display messages in chat about Channel Points rewards.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.points.show-button' , {
default : true ,
ui : {
path : 'Chat > Channel Points >> General' ,
title : 'Display Channel Points button beneath chat.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.points.show-rewards' , {
default : true ,
ui : {
path : 'Chat > Channel Points >> Behavior' ,
title : 'Allow available rewards to appear next to the Channel Points button.' ,
component : 'setting-check-box'
}
} ) ;
2019-06-18 19:48:51 -04:00
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'
}
} ) ;
2017-11-13 01:23:39 -05:00
this . settings . add ( 'chat.width' , {
default : 340 ,
ui : {
path : 'Chat > Appearance >> General @{"sort": -1}' ,
title : 'Width' ,
2018-01-16 17:36:56 -05:00
description : "How wide chat should be, in pixels. This may be affected by your browser's zoom and font size settings." ,
2017-11-13 01:23:39 -05:00
component : 'setting-text-box' ,
process ( val ) {
val = parseInt ( val , 10 ) ;
if ( isNaN ( val ) || ! isFinite ( val ) || val <= 0 )
return 340 ;
return val ;
}
}
} ) ;
2019-05-16 14:46:26 -04:00
this . settings . add ( 'chat.use-width' , {
requires : [ 'chat.width' , 'context.ui.rightColumnExpanded' ] ,
process ( ctx ) {
if ( ! ctx . get ( 'context.ui.rightColumnExpanded' ) )
return false ;
return ctx . get ( 'chat.width' ) != 340 ;
}
} ) ;
2017-11-13 01:23:39 -05:00
this . settings . add ( 'chat.bits.show-pinned' , {
2019-06-13 22:56:50 -04:00
requires : [ 'chat.bits.show' ] ,
default : null ,
process ( ctx , val ) {
if ( val != null )
return val ;
return ctx . get ( 'chat.bits.show' )
} ,
2017-11-13 01:23:39 -05:00
ui : {
2020-05-27 15:44:37 -04:00
path : 'Chat > Appearance >> Community' ,
title : 'Display Leaderboard' ,
description : 'The leaderboard shows the top cheerers and sub gifters in a channel.\n\nBy default due to a previous implementation, this inherits its value from Chat > Bits and Cheering > Display Bits.' ,
2019-06-13 22:56:50 -04:00
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.bits.show-rewards' , {
requires : [ 'chat.bits.show' ] ,
default : null ,
process ( ctx , val ) {
if ( val != null )
return val ;
2017-11-13 01:23:39 -05:00
2019-06-13 22:56:50 -04:00
return ctx . get ( 'chat.bits.show' )
} ,
ui : {
path : 'Chat > Bits and Cheering >> Behavior' ,
title : 'Display messages when a cheer shares rewards to people in chat.' ,
description : 'By default, this inherits its value from Display Bits. This setting only affects newly arrived messages.' ,
2017-11-13 01:23:39 -05:00
component : 'setting-check-box'
}
} ) ;
2018-02-02 16:00:58 -05:00
this . settings . add ( 'chat.rituals.show' , {
default : true ,
ui : {
path : 'Chat > Filtering >> Rituals' ,
title : 'Display ritual messages such as "User is new here! Say Hello!".' ,
component : 'setting-check-box'
}
} ) ;
2019-02-05 14:24:45 -05:00
this . settings . add ( 'chat.subs.show' , {
default : 3 ,
ui : {
path : 'Chat > Appearance >> Subscriptions' ,
title : 'Display Subs in Chat' ,
component : 'setting-select-box' ,
description : '**Note**: Messages sent with re-subs will always be displayed. This only controls the special "X subscribed!" message.' ,
data : [
{ value : 0 , title : 'Do Not Display' } ,
{ value : 1 , title : 'Re-Subs with Messages Only' } ,
{ value : 2 , title : 'Re-Subs Only' } ,
{ value : 3 , title : 'Display All' }
]
}
} ) ;
this . settings . add ( 'chat.subs.compact' , {
default : false ,
ui : {
path : 'Chat > Appearance >> Subscriptions' ,
title : 'Display subscription notices in a more compact (classic style) form.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.subs.merge-gifts' , {
default : 1000 ,
ui : {
path : 'Chat > Appearance >> Subscriptions' ,
title : 'Merge Mass Sub Gifts' ,
component : 'setting-select-box' ,
data : [
{ value : 1000 , title : 'Disabled' } ,
{ value : 50 , title : 'More than 50' } ,
{ value : 20 , title : 'More than 20' } ,
{ value : 10 , title : 'More than 10' } ,
{ value : 5 , title : 'More than 5' } ,
{ value : 0 , title : 'Always' }
] ,
description : 'Merge mass gift subscriptions into a single message, depending on the quantity.\n**Note:** Only affects newly gifted subs.'
}
} ) ;
2019-03-14 21:43:44 -04:00
this . settings . add ( 'chat.subs.merge-gifts-visibility' , {
default : false ,
ui : {
path : 'Chat > Appearance >> Subscriptions' ,
title : 'Expand merged mass sub gift messages by default.' ,
component : 'setting-check-box'
}
} ) ;
2017-11-13 01:23:39 -05:00
this . settings . add ( 'chat.lines.alternate' , {
default : false ,
ui : {
path : 'Chat > Appearance >> Chat Lines' ,
title : 'Display lines with alternating background colors.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.lines.padding' , {
default : false ,
ui : {
path : 'Chat > Appearance >> Chat Lines' ,
title : 'Reduce padding around lines.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.lines.borders' , {
default : 0 ,
ui : {
path : 'Chat > Appearance >> Chat Lines' ,
title : 'Separators' ,
component : 'setting-select-box' ,
data : [
{ value : 0 , title : 'Disabled' } ,
{ value : 1 , title : 'Basic Line (1px Solid)' } ,
{ value : 2 , title : '3D Line (2px Groove)' } ,
{ value : 3 , title : '3D Line (2px Groove Inset)' } ,
{ value : 4 , title : 'Wide Line (2px Solid)' }
]
}
} ) ;
}
get currentChat ( ) {
for ( const inst of this . ChatController . instances )
if ( inst && inst . chatService )
return inst ;
2019-06-20 15:15:54 -04:00
return null ;
2017-11-13 01:23:39 -05:00
}
updateColors ( ) {
const is _dark = this . chat . context . get ( 'theme.is-dark' ) ,
mode = this . chat . context . get ( 'chat.adjustment-mode' ) ,
contrast = this . chat . context . get ( 'chat.adjustment-contrast' ) ,
2018-04-28 17:56:03 -04:00
c = this . colors ,
ic = this . inverse _colors ;
2017-11-13 01:23:39 -05:00
// TODO: Get the background color from the theme system.
2017-11-20 16:59:52 -05:00
// Updated: Use the lightest/darkest colors from alternating rows for better readibility.
c . _base = is _dark ? '#191919' : '#e0e0e0' ; //#0e0c13' : '#faf9fa';
2017-11-13 01:23:39 -05:00
c . mode = mode ;
c . contrast = contrast ;
2018-04-28 17:56:03 -04:00
ic . _base = is _dark ? '#dad8de' : '#19171c' ;
ic . mode = mode ;
2018-05-31 18:34:15 -04:00
ic . contrast = contrast ;
2018-04-28 17:56:03 -04:00
2017-11-13 01:23:39 -05:00
this . updateChatLines ( ) ;
2019-07-31 17:13:56 -04:00
this . updateMentionCSS ( ) ;
2019-05-31 23:21:49 -04:00
this . emit ( ':update-colors' ) ;
2017-11-13 01:23:39 -05:00
}
2017-11-14 18:48:56 -05:00
updateChatCSS ( ) {
2019-05-16 15:18:39 -04:00
if ( ! this . _update _css _waiter )
this . _update _css _waiter = requestAnimationFrame ( ( ) => this . _updateChatCSS ( ) ) ;
2019-05-16 14:46:26 -04:00
}
_updateChatCSS ( ) {
2019-05-16 15:18:39 -04:00
cancelAnimationFrame ( this . _update _css _waiter ) ;
this . _update _css _waiter = null ;
2019-05-16 14:46:26 -04:00
2017-11-14 18:48:56 -05:00
const width = this . chat . context . get ( 'chat.width' ) ,
size = this . chat . context . get ( 'chat.font-size' ) ,
2018-04-08 21:20:46 +02:00
emote _alignment = this . chat . context . get ( 'chat.lines.emote-alignment' ) ,
2017-11-17 14:59:46 -05:00
lh = Math . round ( ( 20 / 12 ) * size ) ;
2017-11-14 18:48:56 -05:00
2017-11-17 14:59:46 -05:00
let font = this . chat . context . get ( 'chat.font-family' ) || 'inherit' ;
if ( font . indexOf ( ' ' ) !== - 1 && font . indexOf ( ',' ) === - 1 && font . indexOf ( '"' ) === - 1 && font . indexOf ( "'" ) === - 1 )
font = ` " ${ font } " ` ;
2017-11-14 18:48:56 -05:00
2018-01-16 17:36:56 -05:00
this . css _tweaks . setVariable ( 'chat-font-size' , ` ${ size / 10 } rem ` ) ;
this . css _tweaks . setVariable ( 'chat-line-height' , ` ${ lh / 10 } rem ` ) ;
2017-11-17 14:59:46 -05:00
this . css _tweaks . setVariable ( 'chat-font-family' , font ) ;
2018-01-16 17:36:56 -05:00
this . css _tweaks . setVariable ( 'chat-width' , ` ${ width / 10 } rem ` ) ;
2020-05-27 15:44:37 -04:00
this . css _tweaks . setVariable ( 'negative-chat-width' , ` ${ - width / 10 } rem ` ) ;
2017-11-14 18:48:56 -05:00
2017-11-17 17:31:21 -05:00
this . css _tweaks . toggle ( 'chat-font' , size !== 12 || font ) ;
2019-05-16 15:10:34 -04:00
this . css _tweaks . toggle ( 'chat-width' , this . settings . get ( 'chat.use-width' ) ) ;
2018-04-08 21:20:46 +02:00
this . css _tweaks . toggle ( 'emote-alignment-padded' , emote _alignment === 1 ) ;
this . css _tweaks . toggle ( 'emote-alignment-baseline' , emote _alignment === 2 ) ;
2019-08-24 00:01:46 +02:00
this . emit ( ':update-chat-css' ) ;
2020-05-27 15:44:37 -04:00
this . emit ( 'site.player:fix-player' ) ;
2017-11-13 01:23:39 -05:00
}
updateLineBorders ( ) {
const mode = this . chat . context . get ( 'chat.lines.borders' ) ;
this . css _tweaks . toggle ( 'chat-borders' , mode > 0 ) ;
this . css _tweaks . toggle ( 'chat-borders-3d' , mode === 2 ) ;
this . css _tweaks . toggle ( 'chat-borders-3d-inset' , mode === 3 ) ;
this . css _tweaks . toggle ( 'chat-borders-wide' , mode === 4 ) ;
}
2017-11-17 14:59:46 -05:00
updateMentionCSS ( ) {
const enabled = this . chat . context . get ( 'chat.filtering.highlight-mentions' ) ;
this . css _tweaks . toggle ( 'chat-mention-token' , this . chat . context . get ( 'chat.filtering.highlight-tokens' ) ) ;
2019-07-31 17:13:56 -04:00
const raw _color = this . chat . context . get ( 'chat.filtering.mention-color' ) ;
if ( raw _color ) {
this . css _tweaks . toggle ( 'chat-mention-bg' , false ) ;
this . css _tweaks . toggle ( 'chat-mention-bg-alt' , false ) ;
this . css _tweaks . toggle ( 'chat-mention-bg-custom' , true ) ;
this . css _tweaks . setVariable ( 'chat-mention-color' , this . inverse _colors . process ( raw _color ) ) ;
} else {
this . css _tweaks . toggle ( 'chat-mention-bg-custom' , false ) ;
this . css _tweaks . toggle ( 'chat-mention-bg' , enabled ) ;
this . css _tweaks . toggle ( 'chat-mention-bg-alt' , enabled && this . chat . context . get ( 'chat.lines.alternate' ) ) ;
}
2017-11-17 14:59:46 -05:00
}
2017-11-13 01:23:39 -05:00
2019-11-11 14:38:49 -05:00
updatePointsInfo ( inst ) {
const icon = inst ? . pointIcon ,
name = inst ? . pointName ;
if ( icon ) {
this . css _tweaks . set ( 'points-icon' , ` .ffz--points-icon:before { display: none }
. ffz -- points - icon : after {
display : inline - block ;
margin : 0 0.5 rem - 0.6 rem ;
background - image : url ( "${icon.url}" ) ;
background - image : $ { WEBKIT } image - set ( url ( "${icon.url}" ) 1 x , url ( "${icon.url2x}" ) 2 x , url ( "${icon.url4x}" ) 4 x ) ;
} ` );
} else
this . css _tweaks . delete ( 'points-icon' ) ;
this . point _name = name || null ;
}
2018-05-18 17:48:10 -04:00
async grabTypes ( ) {
2018-07-13 14:32:12 -04:00
const ct = await this . web _munch . findModule ( 'chat-types' ) ,
changes = [ ] ;
2018-07-13 20:39:01 -04:00
this . automod _types = ct && ct . a || AUTOMOD _TYPES ;
this . chat _types = ct && ct . b || CHAT _TYPES ;
this . message _types = ct && ct . c || MESSAGE _TYPES ;
this . mod _types = ct && ct . e || MOD _TYPES ;
2018-07-13 14:32:12 -04:00
if ( ! ct )
return ;
2018-07-13 20:39:01 -04:00
if ( ct . a && ! shallow _object _equals ( ct . a , AUTOMOD _TYPES ) )
2018-07-13 14:32:12 -04:00
changes . push ( 'AUTOMOD_TYPES' ) ;
2018-07-13 20:39:01 -04:00
if ( ct . b && ! shallow _object _equals ( ct . b , CHAT _TYPES ) )
2018-07-13 14:32:12 -04:00
changes . push ( 'CHAT_TYPES' ) ;
2018-07-13 20:39:01 -04:00
if ( ct . c && ! shallow _object _equals ( ct . c , MESSAGE _TYPES ) )
2018-07-13 14:32:12 -04:00
changes . push ( 'MESSAGE_TYPES' ) ;
2018-07-13 20:39:01 -04:00
if ( ct . e && ! shallow _object _equals ( ct . e , MOD _TYPES ) )
2018-07-13 14:32:12 -04:00
changes . push ( 'MOD_TYPES' ) ;
if ( changes . length )
this . log . info ( 'Chat Types have changed from static mappings for categories:' , changes . join ( ' ' ) ) ;
2018-04-28 17:56:03 -04:00
}
onEnable ( ) {
this . on ( 'site.web_munch:loaded' , this . grabTypes ) ;
2020-03-05 18:21:11 -05:00
this . on ( 'site.web_munch:loaded' , this . defineClasses ) ;
2018-04-28 17:56:03 -04:00
this . grabTypes ( ) ;
2020-03-05 18:21:11 -05:00
this . defineClasses ( ) ;
2017-11-14 22:13:30 -05:00
2019-09-13 17:03:50 -04:00
this . chat . context . on ( 'changed:chat.points.show-callouts' , ( ) => {
this . InlineCallout . forceUpdate ( ) ;
this . CalloutSelector . forceUpdate ( ) ;
} ) ;
this . chat . context . on ( 'changed:chat.points.show-button' , ( ) => this . PointsButton . forceUpdate ( ) ) ;
this . chat . context . on ( 'changed:chat.points.show-rewards' , ( ) => {
this . PointsButton . forceUpdate ( ) ;
this . PointsClaimButton . forceUpdate ( ) ;
} ) ;
2020-03-06 01:44:33 -05:00
this . chat . context . on ( 'changed:chat.banners.hype-train' , this . cleanHighlights , this ) ;
this . chat . context . on ( 'changed:chat.subs.gift-banner' , this . cleanHighlights , this ) ;
this . chat . context . on ( 'changed:chat.banners.polls' , this . cleanHighlights , this ) ;
2019-11-11 14:38:49 -05:00
this . chat . context . on ( 'changed:chat.subs.gift-banner' , ( ) => this . GiftBanner . forceUpdate ( ) , this ) ;
2019-05-16 15:18:39 -04:00
this . chat . context . on ( 'changed:chat.width' , this . updateChatCSS , this ) ;
2019-05-24 22:46:34 -04:00
this . settings . main _context . on ( 'changed:chat.use-width' , this . updateChatCSS , this ) ;
2017-11-14 18:48:56 -05:00
this . chat . context . on ( 'changed:chat.font-size' , this . updateChatCSS , this ) ;
this . chat . context . on ( 'changed:chat.font-family' , this . updateChatCSS , this ) ;
2018-04-08 15:51:50 -04:00
this . chat . context . on ( 'changed:chat.lines.emote-alignment' , this . updateChatCSS , this ) ;
2017-11-13 01:23:39 -05:00
this . chat . context . on ( 'changed:chat.adjustment-mode' , this . updateColors , this ) ;
this . chat . context . on ( 'changed:chat.adjustment-contrast' , this . updateColors , this ) ;
this . chat . context . on ( 'changed:theme.is-dark' , this . updateColors , this ) ;
this . chat . context . on ( 'changed:chat.lines.borders' , this . updateLineBorders , this ) ;
2017-11-17 14:59:46 -05:00
this . chat . context . on ( 'changed:chat.filtering.highlight-mentions' , this . updateMentionCSS , this ) ;
this . chat . context . on ( 'changed:chat.filtering.highlight-tokens' , this . updateMentionCSS , this ) ;
2019-07-31 17:13:56 -04:00
this . chat . context . on ( 'changed:chat.filtering.mention-color' , this . updateMentionCSS , this ) ;
2017-12-13 20:22:11 -05:00
this . chat . context . on ( 'changed:chat.fix-bad-emotes' , this . updateChatLines , this ) ;
2020-01-22 16:58:55 -05:00
this . chat . context . on ( 'changed:chat.points.allow-highlight' , this . updateChatLines , this ) ;
2019-04-18 21:07:11 -04:00
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 ) ;
2019-05-31 16:05:50 -04:00
this . chat . context . on ( 'changed:chat.filtering.clickable-mentions' , val => this . css _tweaks . toggle ( 'clickable-mentions' , val ) ) ;
2020-06-23 17:17:00 -04:00
this . chat . context . on ( 'changed:chat.filtering.bold-mentions' , val => this . css _tweaks . toggle ( 'chat-mention-no-bold' , ! val ) ) ;
2019-06-18 19:48:51 -04:00
this . chat . context . on ( 'changed:chat.pin-resubs' , val => {
if ( val ) {
this . updateInlineCallouts ( ) ;
this . updatePinnedCallouts ( ) ;
}
} , this ) ;
2017-11-13 01:23:39 -05:00
2019-09-23 12:17:51 -04:00
this . chat . context . on ( 'changed:chat.community-chest.show' , ( ) => {
this . CommunityChestBanner . forceUpdate ( ) ;
this . CalloutSelector . forceUpdate ( ) ;
} , this ) ;
2017-11-17 14:59:46 -05:00
this . chat . context . on ( 'changed:chat.lines.alternate' , val => {
this . css _tweaks . toggle ( 'chat-rows' , val ) ;
this . updateMentionCSS ( ) ;
} ) ;
2017-11-13 01:23:39 -05:00
this . chat . context . on ( 'changed:chat.lines.padding' , val =>
this . css _tweaks . toggle ( 'chat-padding' , val ) ) ;
this . chat . context . on ( 'changed:chat.bits.show' , val =>
this . css _tweaks . toggle ( 'hide-bits' , ! val ) ) ;
this . chat . context . on ( 'changed:chat.bits.show-pinned' , val =>
this . css _tweaks . toggleHide ( 'pinned-cheer' , ! val ) ) ;
2019-04-18 03:16:19 -04:00
this . chat . context . on ( 'changed:chat.filtering.deleted-style' , val => {
this . css _tweaks . toggle ( 'chat-deleted-strike' , val === 1 || val === 2 ) ;
this . css _tweaks . toggle ( 'chat-deleted-fade' , val < 2 ) ;
} ) ;
2018-08-02 14:29:18 -04:00
2019-04-18 03:16:19 -04:00
const val = this . chat . context . get ( 'chat.filtering.deleted-style' ) ;
this . css _tweaks . toggle ( 'chat-deleted-strike' , val === 1 || val === 2 ) ;
this . css _tweaks . toggle ( 'chat-deleted-fade' , val < 2 ) ;
2018-08-02 14:29:18 -04:00
2019-05-31 16:05:50 -04:00
this . css _tweaks . toggle ( 'clickable-mentions' , this . chat . context . get ( 'chat.filtering.clickable-mentions' ) ) ;
2020-06-23 17:17:00 -04:00
this . css _tweaks . toggle ( 'chat-mention-no-bold' , ! this . chat . context . get ( 'chat.filtering.bold-mentions' ) ) ;
2019-05-31 16:05:50 -04:00
2019-12-10 20:46:33 -05:00
this . chat . context . on ( 'changed:chat.hide-community-highlights' , val => this . css _tweaks . toggleHide ( 'community-highlights' , val ) ) ;
this . css _tweaks . toggleHide ( 'community-highlights' , this . chat . context . get ( 'chat.hide-community-highlights' ) ) ;
2017-11-13 01:23:39 -05:00
this . css _tweaks . toggleHide ( 'pinned-cheer' , ! this . chat . context . get ( 'chat.bits.show-pinned' ) ) ;
this . css _tweaks . toggle ( 'hide-bits' , ! this . chat . context . get ( 'chat.bits.show' ) ) ;
this . css _tweaks . toggle ( 'chat-rows' , this . chat . context . get ( 'chat.lines.alternate' ) ) ;
this . css _tweaks . toggle ( 'chat-padding' , this . chat . context . get ( 'chat.lines.padding' ) ) ;
2017-11-14 18:48:56 -05:00
this . updateChatCSS ( ) ;
2017-11-13 01:23:39 -05:00
this . updateColors ( ) ;
this . updateLineBorders ( ) ;
2017-11-17 14:59:46 -05:00
this . updateMentionCSS ( ) ;
2017-11-13 01:23:39 -05:00
2020-06-30 19:48:46 -04:00
this . RaidController . on ( 'mount' , this . wrapRaidController , this ) ;
this . RaidController . on ( 'update' , this . noAutoRaids , this ) ;
this . RaidController . ready ( ( cls , instances ) => {
for ( const inst of instances )
this . wrapRaidController ( inst ) ;
} ) ;
2019-06-18 19:48:51 -04:00
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 ( ) ) ;
2019-09-13 17:03:50 -04:00
const t = this ;
2019-11-11 14:38:49 -05:00
this . PointsInfo . on ( 'mount' , this . updatePointsInfo , this ) ;
this . PointsInfo . on ( 'update' , this . updatePointsInfo , this ) ;
this . PointsInfo . on ( 'unmount' , ( ) => this . updatePointsInfo ( null ) ) ;
this . PointsInfo . ready ( ( ) => this . updatePointsInfo ( this . PointsInfo . first ) ) ;
this . GiftBanner . ready ( cls => {
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
if ( ! t . chat . context . get ( 'chat.subs.gift-banner' ) )
return null ;
return old _render . call ( this ) ;
}
this . GiftBanner . forceUpdate ( ) ;
} ) ;
2019-09-23 12:17:51 -04:00
this . CommunityChestBanner . ready ( cls => {
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
try {
if ( ! t . chat . context . get ( 'chat.community-chest.show' ) )
return null ;
} catch ( err ) {
t . log . capture ( err ) ;
t . log . error ( err ) ;
}
return old _render . call ( this ) ;
} ;
this . CommunityChestBanner . forceUpdate ( ) ;
} ) ;
2019-09-13 17:03:50 -04:00
this . InlineCallout . ready ( cls => {
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
try {
2019-10-14 00:58:00 -04:00
const callout = this . props ? . event ? . callout ,
ctype = callout ? . trackingType ;
if ( ctype === 'community_points_reward' && ! t . chat . context . get ( 'chat.points.show-callouts' ) )
return null ;
if ( ctype === 'prime_gift_bomb' && ! t . chat . context . get ( 'chat.community-chest.show' ) )
2019-09-13 17:03:50 -04:00
return null ;
2019-10-14 00:58:00 -04:00
if ( ctype === 'megacheer_emote_recipient' && ! t . chat . context . get ( 'chat.bits.show-rewards' ) )
2019-09-23 12:17:51 -04:00
return null ;
2019-09-13 17:03:50 -04:00
} catch ( err ) {
t . log . capture ( err ) ;
t . log . error ( err ) ;
}
return old _render . call ( this ) ;
}
this . InlineCallout . forceUpdate ( ) ;
2019-09-23 12:17:51 -04:00
} ) ;
2019-09-13 17:03:50 -04:00
this . CalloutSelector . ready ( cls => {
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
try {
const callout = this . props . callouts [ 0 ] || this . props . pinnedCallout ,
ctype = callout ? . event ? . type ;
2019-09-23 12:17:51 -04:00
if ( ctype === 'prime-gift-bomb-gifter' && ! t . chat . context . get ( 'chat.community-chest.show' ) )
return null ;
2019-09-13 17:03:50 -04:00
if ( ctype === 'community-points-rewards' && ! t . chat . context . get ( 'chat.points.show-callouts' ) )
return null ;
2019-10-14 00:58:00 -04:00
if ( ( ctype === 'mega-recipient-rewards' || ctype === 'mega-benefactor-rewards' ) && ! t . chat . context . get ( 'chat.bits.show-rewards' ) )
return null ;
2019-09-13 17:03:50 -04:00
} catch ( err ) {
t . log . capture ( err ) ;
t . log . error ( err ) ;
}
return old _render . call ( this ) ;
}
this . CalloutSelector . forceUpdate ( ) ;
} ) ;
this . PointsButton . ready ( cls => {
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
try {
if ( ! t . chat . context . get ( 'chat.points.show-button' ) )
return null ;
if ( ! t . chat . context . get ( 'chat.points.show-rewards' ) ) {
const aq = this . state . animationQueue ;
this . state . animationQueue = [ ] ;
const out = old _render . call ( this ) ;
this . state . animationQueue = aq ;
return out ;
}
} catch ( err ) {
t . log . capture ( err ) ;
t . log . error ( err ) ;
}
return old _render . call ( this ) ;
}
this . PointsButton . forceUpdate ( ) ;
} ) ;
this . PointsClaimButton . ready ( cls => {
2019-11-11 14:38:49 -05:00
cls . prototype . ffzHasOffer = function ( ) {
return ! this . props . hidden && ! this . state ? . error && this . getClaim ( ) != null ;
} ;
2019-09-13 17:03:50 -04:00
const old _render = cls . prototype . render ;
cls . prototype . render = function ( ) {
try {
2019-11-11 14:38:49 -05:00
if ( this . ffzHasOffer ( ) && ! this . _ffz _timer && t . chat . context . get ( 'chat.points.auto-rewards' ) )
2019-09-13 17:03:50 -04:00
this . _ffz _timer = setTimeout ( ( ) => {
this . _ffz _timer = null ;
2019-11-11 14:38:49 -05:00
if ( this . onClick && this . ffzHasOffer ( ) )
2019-09-13 17:03:50 -04:00
this . onClick ( ) ;
} , 1000 + Math . floor ( Math . random ( ) * 5000 ) ) ;
if ( ! t . chat . context . get ( 'chat.points.show-rewards' ) )
return null ;
} catch ( err ) {
t . log . capture ( err ) ;
t . log . error ( err ) ;
}
return old _render . call ( this ) ;
}
this . PointsClaimButton . forceUpdate ( ) ;
} ) ;
2017-11-13 01:23:39 -05:00
this . ChatController . on ( 'mount' , this . chatMounted , this ) ;
2019-06-14 21:24:48 -04:00
this . ChatController . on ( 'unmount' , this . chatUnmounted , this ) ;
2019-11-11 14:38:49 -05:00
//this.ChatController.on('receive-props', this.chatUpdated, this);
this . ChatController . on ( 'update' , this . chatUpdated , this ) ;
2017-11-13 01:23:39 -05:00
2018-08-28 19:13:26 -04:00
this . ChatService . ready ( ( cls , instances ) => {
this . wrapChatService ( cls ) ;
for ( const inst of instances ) {
inst . client . events . removeAll ( ) ;
2018-10-14 13:12:51 -04:00
inst . _ffzInstall ( ) ;
2020-03-31 18:14:27 -04:00
const channel = inst . joinedChannel ,
state = inst . client ? . session ? . channelstate ? . [ ` # ${ channel } ` ] ? . roomState ;
if ( state )
this . updateChatState ( state ) ;
2018-08-28 19:13:26 -04:00
inst . connectHandlers ( ) ;
2018-10-14 13:12:51 -04:00
inst . props . setChatConnectionAPI ( {
sendMessage : inst . sendMessage ,
_ffz _inst : inst
} ) ;
2018-08-28 19:13:26 -04:00
}
} ) ;
this . ChatBuffer . ready ( ( cls , instances ) => {
this . wrapChatBuffer ( cls ) ;
for ( const inst of instances ) {
const handler = inst . props . messageHandlerAPI ;
if ( handler )
handler . removeMessageHandler ( inst . handleMessage ) ;
inst . _ffzInstall ( ) ;
if ( handler )
handler . addMessageHandler ( inst . handleMessage ) ;
2020-03-31 18:14:27 -04:00
// We grab this from the chat client now.
/ * i f ( A r r a y . i s A r r a y ( i n s t . b u f f e r ) ) {
2019-08-27 16:18:12 -04:00
let i = inst . buffer . length ;
const ct = this . chat _types || CHAT _TYPES ;
while ( i -- ) {
const msg = inst . buffer [ i ] ;
if ( msg && msg . type === ct . RoomState && msg . state ) {
2020-03-31 18:14:27 -04:00
this . updateChatState ( msg . state ) ;
2019-08-27 16:18:12 -04:00
break ;
}
}
2020-03-31 18:14:27 -04:00
} * /
2019-08-27 16:18:12 -04:00
2018-08-28 19:13:26 -04:00
inst . props . setMessageBufferAPI ( {
addUpdateHandler : inst . addUpdateHandler ,
removeUpdateHandler : inst . removeUpdateHandler ,
getMessages : inst . getMessages ,
2019-06-03 19:47:41 -04:00
isPaused : inst . isPaused ,
setPaused : inst . setPaused ,
hasNewerLeft : inst . hasNewerLeft ,
loadNewer : inst . loadNewer ,
loadNewest : inst . loadNewest ,
2018-08-28 19:13:26 -04:00
_ffz _inst : inst
} ) ;
}
} ) ;
this . ChatBufferConnector . on ( 'mount' , this . connectorMounted , this ) ;
2019-11-11 14:38:49 -05:00
this . ChatBufferConnector . on ( 'update' , this . connectorUpdated , this ) ;
2018-08-28 19:13:26 -04:00
this . ChatBufferConnector . on ( 'unmount' , this . connectorUnmounted , this ) ;
this . ChatBufferConnector . ready ( ( cls , instances ) => {
for ( const inst of instances )
this . connectorMounted ( inst ) ;
} )
2017-11-13 01:23:39 -05:00
this . ChatController . ready ( ( cls , instances ) => {
2018-03-01 13:40:24 -05:00
const t = this ,
2018-03-03 16:38:50 -05:00
old _catch = cls . prototype . componentDidCatch ,
old _render = cls . prototype . render ;
2018-03-01 13:40:24 -05:00
// Try catching errors. With any luck, maybe we can
// recover from the error when we re-build?
cls . prototype . componentDidCatch = function ( err , info ) {
// Don't log infinitely if stuff gets super screwed up.
const errs = this . state . ffz _errors || 0 ;
if ( errs < 100 ) {
this . setState ( { ffz _errors : errs + 1 } ) ;
t . log . info ( 'Error within Chat' , err , info , errs ) ;
}
if ( old _catch )
return old _catch . call ( this , err , info ) ;
}
2018-03-03 16:38:50 -05:00
cls . prototype . render = function ( ) {
if ( this . state . ffz _errors > 0 ) {
const React = t . web _munch . getModule ( 'react' ) ,
2018-04-01 18:24:08 -04:00
createElement = React && React . createElement ;
2018-03-03 16:38:50 -05:00
2018-04-01 18:24:08 -04:00
if ( ! createElement )
2018-03-03 16:38:50 -05:00
return null ;
2018-04-01 18:24:08 -04:00
return createElement ( 'div' , {
2018-09-25 18:37:14 -04:00
className : 'tw-border-l tw-c-background-alt-2 tw-c-text-base tw-full-width tw-full-height tw-align-items-center tw-flex tw-flex-column tw-justify-content-center tw-relative'
2018-03-03 16:38:50 -05:00
} , 'There was an error displaying chat.' ) ;
} else
return old _render . call ( this ) ;
}
2018-08-28 19:13:26 -04:00
for ( const inst of instances )
2017-11-13 01:23:39 -05:00
this . chatMounted ( inst ) ;
} ) ;
this . ChatContainer . on ( 'mount' , this . containerMounted , this ) ;
2020-06-30 19:48:46 -04:00
this . ChatContainer . on ( 'unmount' , this . containerUnmounted , this ) ; //removeRoom, this);
2019-05-07 15:04:12 -04:00
this . ChatContainer . on ( 'update' , this . containerUpdated , this ) ;
2017-11-13 01:23:39 -05:00
this . ChatContainer . ready ( ( cls , instances ) => {
2018-03-01 13:40:24 -05:00
const t = this ,
2020-03-05 18:21:11 -05:00
old _render = cls . prototype . render ,
2018-03-01 13:40:24 -05:00
old _catch = cls . prototype . componentDidCatch ;
2018-03-03 16:38:50 -05:00
2020-03-05 18:21:11 -05:00
cls . prototype . render = function ( ) {
2020-03-05 18:26:45 -05:00
try {
2020-03-05 18:21:11 -05:00
if ( t . CommunityStackHandler ) {
const React = t . web _munch . getModule ( 'react' ) ,
out = old _render . call ( this ) ,
thing = out ? . props ? . children ? . props ? . children ;
if ( React && Array . isArray ( thing ) )
thing . push ( React . createElement ( t . CommunityStackHandler ) ) ;
return out ;
}
2020-03-05 18:26:45 -05:00
} catch ( err ) {
2020-03-05 18:21:11 -05:00
// No op
2020-03-05 18:26:45 -05:00
}
2020-03-05 18:21:11 -05:00
return old _render . call ( this ) ;
}
2018-03-01 13:40:24 -05:00
// Try catching errors. With any luck, maybe we can
// recover from the error when we re-build?
cls . prototype . componentDidCatch = function ( err , info ) {
// Don't log infinitely if stuff gets super screwed up.
const errs = this . state . ffz _errors || 0 ;
if ( errs < 100 ) {
this . setState ( { ffz _errors : errs + 1 } ) ;
t . log . info ( 'Error within Chat Container' , err , info , errs ) ;
}
if ( old _catch )
return old _catch . call ( this , err , info ) ;
}
2019-05-04 03:36:10 -04:00
2017-11-13 01:23:39 -05:00
for ( const inst of instances )
this . containerMounted ( inst ) ;
} ) ;
2020-06-30 19:48:46 -04:00
}
2017-11-13 01:23:39 -05:00
2020-06-30 19:48:46 -04:00
wrapRaidController ( inst ) {
if ( inst . _ffz _wrapped )
return this . noAutoRaids ( inst ) ;
2017-11-13 01:23:39 -05:00
2020-06-30 19:48:46 -04:00
inst . _ffz _wrapped = true ;
const t = this ,
old _handle _join = inst . handleJoinRaid ;
inst . handleJoinRaid = function ( event , ... args ) {
const raid _id = inst . props && inst . props . raid && inst . props . raid . id ;
if ( event && event . type && raid _id )
t . joined _raids . add ( raid _id ) ;
return old _handle _join . call ( this , event , ... args ) ;
}
this . noAutoRaids ( inst ) ;
}
noAutoRaids ( inst ) {
if ( this . settings . get ( 'channel.raids.no-autojoin' ) )
setTimeout ( ( ) => {
if ( inst . props && inst . props . raid && ! inst . isRaidCreator && inst . hasJoinedCurrentRaid ) {
const id = inst . props . raid . id ;
if ( this . joined _raids . has ( id ) )
return ;
this . log . info ( 'Automatically leaving raid:' , id ) ;
inst . handleLeaveRaid ( ) ;
}
} ) ;
2018-04-28 17:56:03 -04:00
}
2020-03-06 01:44:33 -05:00
cleanHighlights ( ) {
const types = {
'community_sub_gift' : this . chat . context . get ( 'chat.subs.gift-banner' ) ,
'megacheer' : this . chat . context . get ( 'chat.bits.show' ) ,
'hype_train' : this . chat . context . get ( 'chat.banners.hype-train' ) ,
'poll' : this . chat . context . get ( 'chat.banners.polls' )
} ;
const highlights = this . community _stack ? . highlights ;
if ( ! Array . isArray ( highlights ) )
return ;
for ( const entry of highlights ) {
if ( ! entry || ! entry . event || ! entry . id )
continue ;
const type = entry . event . type ;
if ( type && has ( types , type ) && ! types [ type ] )
this . community _dispatch ( {
type : 'remove-highlight' ,
id : entry . id
} ) ;
}
}
2020-03-05 18:21:11 -05:00
defineClasses ( ) {
if ( this . CommunityStackHandler )
return true ;
const t = this ,
React = this . web _munch . getModule ( 'react' ) ,
Stack = this . web _munch . getModule ( 'highlightstack' ) ,
createElement = React && React . createElement ;
if ( ! createElement || ! Stack || ! Stack . b )
return false ;
this . CommunityStackHandler = function ( ) {
const stack = React . useContext ( Stack . b ) ,
dispatch = React . useContext ( Stack . c ) ;
t . community _stack = stack ;
t . community _dispatch = dispatch ;
2020-03-06 01:44:33 -05:00
t . cleanHighlights ( ) ;
2020-03-05 18:21:11 -05:00
return null ;
}
this . ChatContainer . forceUpdate ( ) ;
}
2020-03-31 18:14:27 -04:00
updateChatState ( state ) {
const old _state = this . chat . context . get ( 'context.chat_state' ) || { } ;
if ( deep _equals ( state , old _state ) )
return ;
this . chat . context . updateContext ( {
chat _state : state
} ) ;
this . input . updateInput ( ) ;
}
2019-06-18 19:48:51 -04:00
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 ( ) ;
}
2019-05-08 15:39:14 -04:00
tryUpdateBadges ( ) {
2019-05-08 22:47:38 -04:00
if ( ! this . _badge _timer )
this . _badge _timer = setTimeout ( ( ) => this . _tryUpdateBadges ( ) , 0 ) ;
}
_tryUpdateBadges ( ) {
if ( this . _badge _timer )
clearTimeout ( this . _badge _timer ) ;
this . _badge _timer = null ;
this . log . info ( 'Trying to update badge data from the chat container.' ) ;
2019-05-08 15:39:14 -04:00
const inst = this . ChatContainer . first ;
if ( inst )
this . containerUpdated ( inst , inst . props ) ;
}
2017-11-14 22:13:30 -05:00
wrapChatBuffer ( cls ) {
2018-08-28 19:13:26 -04:00
if ( cls . prototype . _ffz _was _here )
return ;
2018-07-14 14:13:28 -04:00
const t = this ,
2019-06-03 19:47:41 -04:00
old _clear = cls . prototype . clear ,
old _flush = cls . prototype . flushRawMessages ,
2018-08-28 19:13:26 -04:00
old _mount = cls . prototype . componentDidMount ;
2017-11-13 01:23:39 -05:00
2018-08-28 19:13:26 -04:00
cls . prototype . _ffzInstall = function ( ) {
if ( this . _ffz _installed )
return ;
this . _ffz _installed = true ;
2018-08-27 20:15:43 -04:00
2018-08-28 19:13:26 -04:00
const inst = this ,
old _handle = inst . handleMessage ,
old _set = inst . props . setMessageBufferAPI ;
inst . props . setMessageBufferAPI = function ( api ) {
if ( api )
api . _ffz _inst = inst ;
return old _set ( api ) ;
}
inst . handleMessage = function ( msg ) {
2018-07-16 15:07:22 -04:00
if ( msg ) {
try {
2018-10-16 02:13:14 -04:00
const types = t . chat _types || { } ,
mod _types = t . mod _types || { } ;
2018-07-14 14:13:28 -04:00
2019-06-13 22:56:50 -04:00
if ( msg . type === types . RewardGift && ! t . chat . context . get ( 'chat.bits.show-rewards' ) )
return ;
2018-07-16 15:07:22 -04:00
if ( msg . type === types . Message ) {
const m = t . chat . standardizeMessage ( msg ) ,
2018-08-28 19:13:26 -04:00
cont = inst . _ffz _connector ,
2018-08-28 09:45:53 -04:00
room _id = cont && cont . props . channelID ;
2018-08-27 20:15:43 -04:00
2018-08-28 19:13:26 -04:00
let room = m . roomLogin = m . roomLogin ? m . roomLogin : m . channel ? m . channel . slice ( 1 ) : cont && cont . props . channelLogin ;
2018-07-16 15:07:22 -04:00
if ( ! room && room _id ) {
const r = t . chat . getRoom ( room _id , null , true ) ;
if ( r && r . login )
room = m . roomLogin = r . login ;
}
2018-07-14 14:13:28 -04:00
2018-07-16 15:07:22 -04:00
const u = t . site . getUser ( ) ,
r = { id : room _id , login : room } ;
2018-07-14 14:13:28 -04:00
2018-07-16 15:07:22 -04:00
if ( u && cont ) {
u . moderator = cont . props . isCurrentUserModerator ;
u . staff = cont . props . isStaff ;
}
m . ffz _tokens = m . ffz _tokens || t . chat . tokenizeMessage ( m , u , r ) ;
const event = new FFZEvent ( {
message : m ,
2018-08-28 19:13:26 -04:00
channel : room ,
channelID : room _id
2018-07-16 15:07:22 -04:00
} ) ;
t . emit ( 'chat:receive-message' , event ) ;
if ( event . defaultPrevented || m . ffz _removed )
return ;
2018-07-14 14:13:28 -04:00
2019-05-07 15:04:12 -04:00
} else if ( msg . type === types . ModerationAction && inst . markUserEventDeleted && inst . unsetModeratedUser ) {
2019-09-23 12:17:51 -04:00
if ( ! ( ( ! msg . level || ! msg . level . length ) && msg . targetUserLogin && msg . targetUserLogin === inst . props . currentUserLogin ) ) {
//t.log.info('Moderation Action', msg);
if ( ! inst . props . isCurrentUserModerator )
2019-04-18 21:07:11 -04:00
return ;
2019-09-23 12:17:51 -04:00
const mod _action = msg . moderationActionType ;
if ( mod _action === 'ban' || mod _action === 'timeout' || mod _action === 'delete' ) {
const user = msg . targetUserLogin ;
if ( inst . moderatedUsers . has ( user ) )
return ;
const do _remove = t . chat . context . get ( 'chat.filtering.remove-deleted' ) === 3 ;
if ( do _remove ) {
const len = inst . buffer . length ,
target _id = msg . messageID ;
inst . buffer = inst . buffer . filter ( m =>
m . type !== types . Message || ! m . user || m . user . userLogin !== user ||
( target _id && m . id !== target _id )
) ;
if ( len !== inst . buffer . length && ! inst . props . isBackground )
inst . notifySubscribers ( ) ;
inst . ffzModerateBuffer ( [ inst . delayedMessageBuffer ] , msg ) ;
} else
inst . ffzModerateBuffer ( [ inst . buffer , inst . delayedMessageBuffer ] , msg ) ;
inst . moderatedUsers . add ( user ) ;
setTimeout ( inst . unsetModeratedUser ( user ) , 1e3 ) ;
inst . delayedMessageBuffer . push ( {
event : msg ,
time : Date . now ( ) ,
shouldDelay : false
} ) ;
2019-04-18 21:07:11 -04:00
2019-09-23 12:17:51 -04:00
return ;
}
2019-04-18 21:07:11 -04:00
}
2019-04-18 03:16:19 -04:00
2019-05-07 15:04:12 -04:00
} else if ( msg . type === types . Moderation && inst . markUserEventDeleted && inst . unsetModeratedUser ) {
2019-04-18 21:07:11 -04:00
//t.log.info('Moderation', msg);
if ( inst . props . isCurrentUserModerator )
2018-07-16 15:07:22 -04:00
return ;
2018-07-14 14:13:28 -04:00
2019-04-18 21:07:11 -04:00
const user = msg . userLogin ;
if ( inst . moderatedUsers . has ( user ) )
return ;
2018-07-14 14:13:28 -04:00
2019-04-18 21:07:11 -04:00
const mod _action = msg . moderationType ;
let new _action ;
if ( mod _action === mod _types . Ban )
new _action = 'ban' ;
else if ( mod _action === mod _types . Delete )
new _action = 'delete' ;
else if ( mod _action === mod _types . Unban )
new _action = 'unban' ;
else if ( mod _action === mod _types . Timeout )
new _action = 'timeout' ;
if ( new _action )
msg . moderationActionType = new _action ;
const do _remove = t . chat . context . get ( 'chat.filtering.remove-deleted' ) === 3 ;
2018-07-16 15:07:22 -04:00
if ( do _remove ) {
2019-04-18 21:07:11 -04:00
const len = inst . buffer . length ,
target _id = msg . targetMessageID ;
inst . buffer = inst . buffer . filter ( m =>
m . type !== types . Message || ! m . user || m . user . userLogin !== user ||
( target _id && m . id !== target _id )
) ;
2018-08-28 19:13:26 -04:00
if ( len !== inst . buffer . length && ! inst . props . isBackground )
inst . notifySubscribers ( ) ;
2018-07-16 15:07:22 -04:00
2019-04-18 21:07:11 -04:00
inst . ffzModerateBuffer ( [ inst . delayedMessageBuffer ] , msg ) ;
2018-07-16 15:07:22 -04:00
} else
2019-04-18 21:07:11 -04:00
inst . ffzModerateBuffer ( [ inst . buffer , inst . delayedMessageBuffer ] , msg ) ;
inst . moderatedUsers . add ( user ) ;
setTimeout ( inst . unsetModeratedUser ( user ) , 1e3 ) ;
2018-07-16 15:07:22 -04:00
2019-04-18 21:07:11 -04:00
inst . delayedMessageBuffer . push ( {
event : msg ,
time : Date . now ( ) ,
shouldDelay : false
} ) ;
2018-07-16 15:07:22 -04:00
2019-04-18 21:07:11 -04:00
return ;
2018-07-14 14:13:28 -04:00
2018-07-16 15:07:22 -04:00
} else if ( msg . type === types . Clear ) {
if ( t . chat . context . get ( 'chat.filtering.ignore-clear' ) )
msg = {
2019-04-18 03:16:19 -04:00
type : types . Info ,
message : t . i18n . t ( 'chat.ignore-clear' , 'An attempt by a moderator to clear chat was ignored.' )
2018-07-16 15:07:22 -04:00
}
}
} catch ( err ) {
2018-08-28 19:13:26 -04:00
t . log . error ( 'Error processing chat event.' , err ) ;
t . log . capture ( err , { extra : { msg } } ) ;
2018-07-16 15:07:22 -04:00
}
2018-07-14 14:13:28 -04:00
}
2018-07-16 15:07:22 -04:00
2018-08-28 19:13:26 -04:00
return old _handle . call ( inst , msg ) ;
2018-07-16 15:07:22 -04:00
}
2019-04-18 21:07:11 -04:00
inst . ffzModerateBuffer = function ( buffers , event ) {
const mod _types = t . mod _types || { } ,
mod _type = event . moderationActionType ,
user _login = event . targetUserLogin || event . userLogin ,
mod _login = event . createdByLogin ,
target _id = event . targetMessageID || event . messageID ;
let deleted _count = 0 , last _msg ;
const is _delete = mod _type === mod _types . Delete ,
updater = m => {
if ( m . event )
m = m . event ;
if ( target _id && m . id !== target _id )
return ;
const msg = inst . markUserEventDeleted ( m , user _login ) ;
if ( ! msg )
return ;
last _msg = msg ;
deleted _count ++ ;
msg . modLogin = mod _login ;
msg . modActionType = mod _type ;
msg . duration = event . duration ;
if ( is _delete )
return true ;
} ;
for ( const buffer of buffers )
if ( buffer . some ( updater ) )
break ;
//t.log.info('Moderate Buffer', mod_type, user_login, mod_login, target_id, deleted_count, last_msg);
if ( last _msg )
last _msg . deletedCount = deleted _count ;
}
2019-06-03 19:47:41 -04:00
inst . setPaused = function ( paused ) {
if ( inst . paused === paused )
return ;
2019-04-18 21:07:11 -04:00
2019-06-03 19:47:41 -04:00
inst . paused = paused ;
if ( ! paused ) {
inst . slidingWindowEnd = Math . min ( inst . buffer . length , t . chat . context . get ( 'chat.scrollback-length' ) ) ;
if ( ! inst . props . isBackground )
inst . notifySubscribers ( ) ;
}
}
2019-04-18 21:07:11 -04:00
2019-06-03 19:47:41 -04:00
inst . loadNewer = function ( ) {
if ( ! inst . hasNewerLeft ( ) )
return ;
2018-08-28 19:13:26 -04:00
2019-06-03 19:47:41 -04:00
const end = Math . min ( inst . buffer . length , inst . slidingWindowEnd + 40 ) ,
start = Math . max ( 0 , end - t . chat . context . get ( 'chat.scrollback-length' ) ) ;
2018-08-28 19:13:26 -04:00
2019-06-03 19:47:41 -04:00
inst . clear ( inst . buffer . length - start ) ;
inst . slidingWindowEnd = end - start ;
if ( ! inst . props . isBackground )
inst . notifySubscribers ( ) ;
}
inst . loadNewest = function ( ) {
if ( ! inst . hasNewerLeft ( ) )
return ;
const max _size = t . chat . context . get ( 'chat.scrollback-length' ) ;
2018-08-28 19:13:26 -04:00
2019-06-03 19:47:41 -04:00
inst . clear ( max _size ) ;
inst . slidingWindowEnd = Math . min ( max _size , inst . buffer . length ) ;
if ( ! inst . props . isBackground )
inst . notifySubscribers ( ) ;
2018-08-28 19:13:26 -04:00
}
2019-06-04 00:50:21 -04:00
inst . getMessages = function ( ) {
return inst . buffer . slice ( 0 , inst . slidingWindowEnd + ( inst . ffz _extra || 0 ) ) ;
}
2018-08-28 19:13:26 -04:00
}
cls . prototype . componentDidMount = function ( ) {
try {
this . _ffzInstall ( ) ;
} catch ( err ) {
t . log . error ( 'Error installing FFZ features onto chat buffer.' , err ) ;
}
return old _mount . call ( this ) ;
}
2019-06-03 19:47:41 -04:00
cls . prototype . clear = function ( count ) {
try {
if ( count == null )
count = 0 ;
const max _size = t . chat . context . get ( 'chat.scrollback-length' ) ;
if ( ! this . isPaused ( ) && count > max _size )
count = max _size ;
if ( count <= 0 ) {
2019-06-04 00:50:21 -04:00
this . ffz _extra = 0 ;
2019-06-03 19:47:41 -04:00
this . buffer = [ ] ;
this . delayedMessageBuffer = [ ] ;
this . paused = false ;
} else {
const buffer = this . buffer ,
ct = t . chat _types || CHAT _TYPES ,
target = buffer . length - count ;
if ( target > 0 ) {
let removed = 0 , last ;
for ( let i = 0 ; i < target ; i ++ )
if ( buffer [ i ] && ! NULL _TYPES . includes ( ct [ buffer [ i ] . type ] ) ) {
removed ++ ;
last = i ;
}
2018-07-16 15:07:22 -04:00
2019-06-04 00:50:21 -04:00
// When we remove less then expected, we want to keep track
// of that so we can return the extra messages from getMessages.
this . buffer = buffer . slice ( removed % 2 !== 0 ? Math . max ( target - 4 , last ) : target ) ;
2019-06-04 12:20:44 -04:00
this . ffz _extra = buffer . length - count ;
2018-07-16 15:07:22 -04:00
2019-06-04 00:50:21 -04:00
} else {
this . ffz _extra = 0 ;
2019-06-03 19:47:41 -04:00
this . buffer = this . buffer . slice ( 0 ) ;
2019-06-04 00:50:21 -04:00
}
2018-07-16 15:07:22 -04:00
2019-06-03 19:47:41 -04:00
if ( this . paused && this . buffer . length >= 900 )
this . setPaused ( false ) ;
}
} catch ( err ) {
t . log . error ( 'Error running clear' , err ) ;
return old _clear . call ( this , count ) ;
2018-07-14 14:13:28 -04:00
}
2019-06-03 19:47:41 -04:00
}
cls . prototype . flushRawMessages = function ( ) {
try {
const out = [ ] ,
now = Date . now ( ) ,
raw _delay = t . chat . context . get ( 'chat.delay' ) ,
delay = raw _delay === - 1 ? this . delayDuration : raw _delay ,
first = now - delay ,
see _deleted = this . shouldSeeBlockedAndDeletedMessages || this . props && this . props . shouldSeeBlockedAndDeletedMessages ,
has _newer = this . hasNewerLeft ( ) ,
paused = this . isPaused ( ) ,
max _size = t . chat . context . get ( 'chat.scrollback-length' ) ,
do _remove = t . chat . context . get ( 'chat.filtering.remove-deleted' ) ;
let added = 0 ,
buffered = this . slidingWindowEnd ,
changed = false ;
for ( const msg of this . delayedMessageBuffer ) {
if ( msg . time <= first || ! msg . shouldDelay ) {
if ( do _remove !== 0 && ( do _remove > 1 || ! see _deleted ) && this . isDeletable ( msg . event ) && msg . event . deleted )
continue ;
this . buffer . push ( msg . event ) ;
changed = true ;
if ( ! this . paused ) {
if ( this . buffer . length > max _size )
added ++ ;
else
buffered ++ ;
}
2018-07-14 14:13:28 -04:00
2019-06-03 19:47:41 -04:00
} else
out . push ( msg ) ;
}
this . delayedMessageBuffer = out ;
if ( changed ) {
this . clear ( Math . min ( 900 , this . buffer . length - added ) ) ;
if ( ! ( added === 0 && buffered === this . slidingWindowEnd && has _newer === this . hasNewerLeft ( ) && paused === this . isPaused ( ) ) ) {
this . slidingWindowEnd = buffered ;
if ( ! this . props . isBackground )
this . notifySubscribers ( ) ;
}
}
} catch ( err ) {
t . log . error ( 'Error running flush.' , err ) ;
return old _flush . call ( this ) ;
}
2017-11-13 01:23:39 -05:00
}
}
2018-04-28 17:56:03 -04:00
sendMessage ( room , message ) {
2018-08-28 19:13:26 -04:00
const service = this . ChatService . first ;
2018-04-28 17:56:03 -04:00
2018-04-29 01:28:19 -04:00
if ( ! service || ! room )
2018-04-28 17:56:03 -04:00
return null ;
if ( room . startsWith ( '#' ) )
room = room . slice ( 1 ) ;
2018-08-28 19:13:26 -04:00
if ( room . toLowerCase ( ) !== service . props . channelLogin . toLowerCase ( ) )
2018-04-29 01:28:19 -04:00
return service . client . sendCommand ( room , message ) ;
2018-04-28 17:56:03 -04:00
service . sendMessage ( message ) ;
}
2017-11-13 01:23:39 -05:00
wrapChatService ( cls ) {
const t = this ,
2018-10-14 13:12:51 -04:00
old _mount = cls . prototype . componentDidMount ,
2018-08-28 19:13:26 -04:00
old _handler = cls . prototype . connectHandlers ;
2017-11-13 01:23:39 -05:00
cls . prototype . _ffz _was _here = true ;
2018-10-14 13:12:51 -04:00
cls . prototype . _ffzInstall = function ( ) {
if ( this . _ffz _installed )
return ;
this . _ffz _installed = true ;
const inst = this ,
old _send = this . sendMessage ;
2019-12-08 16:09:37 -05:00
inst . sendMessage = function ( msg ) {
msg = msg . replace ( /\s+/g , ' ' ) ;
2018-10-14 13:12:51 -04:00
if ( msg . startsWith ( '/ffz' ) ) {
inst . addMessage ( {
type : t . chat _types . Notice ,
message : 'The /ffz command is not yet re-implemented.'
} )
return false ;
}
const event = new FFZEvent ( {
message : msg ,
channel : inst . props . channelLogin
} ) ;
t . emit ( 'chat:pre-send-message' , event ) ;
if ( event . defaultPrevented )
return ;
2019-12-08 16:09:37 -05:00
return old _send . call ( this , event . message ) ;
2018-10-14 13:12:51 -04:00
}
}
cls . prototype . componentDidMount = function ( ) {
try {
this . _ffzInstall ( ) ;
} catch ( err ) {
t . log . error ( 'Error installing FFZ features onto chat service.' , err ) ;
}
return old _mount . call ( this ) ;
}
2017-11-13 01:23:39 -05:00
cls . prototype . connectHandlers = function ( ... args ) {
if ( ! this . _ffz _init ) {
2018-04-28 17:56:03 -04:00
const i = this ;
2017-11-13 01:23:39 -05:00
2018-04-28 17:56:03 -04:00
for ( const key of MISBEHAVING _EVENTS ) {
2017-11-13 01:23:39 -05:00
const original = this [ key ] ;
if ( original )
this [ key ] = function ( e , t ) {
i . _wrapped = e ;
const ret = original . call ( i , e , t ) ;
i . _wrapped = null ;
return ret ;
}
}
2018-06-27 14:13:59 -04:00
const old _chat = this . onChatMessageEvent ;
this . onChatMessageEvent = function ( e ) {
2019-12-08 16:09:37 -05:00
/ * i f ( e & & e . s e n t B y C u r r e n t U s e r ) {
2018-06-27 14:13:59 -04:00
try {
2018-07-14 14:13:28 -04:00
e . message . user . emotes = findEmotes (
2018-06-27 14:13:59 -04:00
e . message . body ,
i . ffzGetEmotes ( )
) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
}
2019-12-08 16:09:37 -05:00
} * /
2018-06-27 14:13:59 -04:00
return old _chat . call ( i , e ) ;
}
const old _action = this . onChatActionEvent ;
this . onChatActionEvent = function ( e ) {
2019-12-08 16:09:37 -05:00
/ * i f ( e & & e . s e n t B y C u r r e n t U s e r ) {
2018-06-27 14:13:59 -04:00
try {
2018-07-14 14:13:28 -04:00
e . message . user . emotes = findEmotes (
2018-07-21 16:26:10 -04:00
e . message . body ,
2018-06-27 14:13:59 -04:00
i . ffzGetEmotes ( )
) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
}
2019-12-08 16:09:37 -05:00
} * /
2018-06-27 14:13:59 -04:00
return old _action . call ( i , e ) ;
}
2019-02-05 14:24:45 -05:00
const old _sub = this . onSubscriptionEvent ;
this . onSubscriptionEvent = function ( e ) {
try {
if ( t . chat . context . get ( 'chat.subs.show' ) < 3 )
return ;
e . body = '' ;
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'resub' ;
out . sub _plan = e . methods ;
return i . postMessageToCurrentChannel ( e , out ) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
return old _sub . call ( i , e ) ;
}
}
2019-08-27 16:18:12 -04:00
const old _state = this . onRoomStateEvent ;
this . onRoomStateEvent = function ( e ) {
try {
const channel = e . channel ,
current = t . chat . context . get ( 'context.channel' ) ;
if ( channel && ( channel === current || channel === ` # ${ current } ` ) )
2020-03-31 18:14:27 -04:00
t . updateChatState ( e . state ) ;
2019-08-27 16:18:12 -04:00
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
}
return old _state . call ( i , e ) ;
}
2017-11-23 02:49:23 -05:00
const old _resub = this . onResubscriptionEvent ;
this . onResubscriptionEvent = function ( e ) {
try {
2019-02-05 14:24:45 -05:00
if ( t . chat . context . get ( 'chat.subs.show' ) < 2 && ! e . body )
return ;
2017-11-23 02:49:23 -05:00
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'resub' ;
2019-01-31 19:58:21 -05:00
out . sub _cumulative = e . cumulativeMonths || 0 ;
out . sub _streak = e . streakMonths || 0 ;
out . sub _share _streak = e . shouldShareStreakTenure ;
2017-11-23 02:49:23 -05:00
out . sub _months = e . months ;
out . sub _plan = e . methods ;
2019-01-31 19:58:21 -05:00
//t.log.info('Resub Event', e, out);
2018-04-28 17:56:03 -04:00
return i . postMessageToCurrentChannel ( e , out ) ;
2017-11-23 02:49:23 -05:00
} catch ( err ) {
2018-04-11 17:05:31 -04:00
t . log . capture ( err , { extra : e } ) ;
2017-11-23 02:49:23 -05:00
return old _resub . call ( i , e ) ;
}
}
2019-02-05 14:24:45 -05:00
const mysteries = this . ffz _mysteries = { } ;
const old _subgift = this . onSubscriptionGiftEvent ;
this . onSubscriptionGiftEvent = function ( e ) {
try {
const key = ` ${ e . channel } : ${ e . user . userID } ` ,
mystery = mysteries [ key ] ;
if ( mystery ) {
if ( mystery . expires < Date . now ( ) ) {
mysteries [ key ] = null ;
} else {
mystery . recipients . push ( {
id : e . recipientID ,
login : e . recipientLogin ,
displayName : e . recipientName
} ) ;
if ( mystery . recipients . length >= mystery . size )
mysteries [ key ] = null ;
if ( mystery . line )
mystery . line . forceUpdate ( ) ;
return ;
}
}
e . body = '' ;
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'sub_gift' ;
out . sub _recipient = {
id : e . recipientID ,
login : e . recipientLogin ,
displayName : e . recipientName
} ;
out . sub _plan = e . methods ;
2019-02-06 14:45:27 -05:00
out . sub _total = e . senderCount ;
2019-02-05 14:24:45 -05:00
//t.log.info('Sub Gift', e, out);
return i . postMessageToCurrentChannel ( e , out ) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
return old _subgift . call ( i , e ) ;
}
}
const old _anonsubgift = this . onAnonSubscriptionGiftEvent ;
this . onAnonSubscriptionGiftEvent = function ( e ) {
try {
const key = ` ${ e . channel } :ANON ` ,
mystery = mysteries [ key ] ;
if ( mystery ) {
if ( mystery . expires < Date . now ( ) )
mysteries [ key ] = null ;
else {
mystery . recipients . push ( {
id : e . recipientID ,
login : e . recipientLogin ,
displayName : e . recipientName
} ) ;
if ( mystery . recipients . length >= mystery . size )
mysteries [ key ] = null ;
if ( mystery . line )
mystery . line . forceUpdate ( ) ;
return ;
}
}
e . body = '' ;
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'sub_gift' ;
out . sub _anon = true ;
out . sub _recipient = {
id : e . recipientID ,
login : e . recipientLogin ,
displayName : e . recipientName
} ;
out . sub _plan = e . methods ;
2019-02-06 14:45:27 -05:00
out . sub _total = e . senderCount ;
2019-02-05 14:24:45 -05:00
//t.log.info('Anon Sub Gift', e, out);
return i . postMessageToCurrentChannel ( e , out ) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
return old _anonsubgift . call ( i , e ) ;
}
}
const old _submystery = this . onSubscriptionMysteryGiftEvent ;
this . onSubscriptionMysteryGiftEvent = function ( e ) {
try {
let mystery = null ;
if ( e . massGiftCount > t . chat . context . get ( 'chat.subs.merge-gifts' ) ) {
const key = ` ${ e . channel } : ${ e . user . userID } ` ;
mystery = mysteries [ key ] = {
recipients : [ ] ,
size : e . massGiftCount ,
expires : Date . now ( ) + 30000
} ;
}
e . body = '' ;
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'sub_mystery' ;
out . mystery = mystery ;
out . sub _plan = e . plan ;
out . sub _count = e . massGiftCount ;
out . sub _total = e . senderCount ;
//t.log.info('Sub Mystery', e, out);
return i . postMessageToCurrentChannel ( e , out ) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
return old _submystery . call ( i , e ) ;
}
}
const old _anonsubmystery = this . onAnonSubscriptionMysteryGiftEvent ;
this . onAnonSubscriptionMysteryGiftEvent = function ( e ) {
try {
let mystery = null ;
if ( e . massGiftCount > t . chat . context . get ( 'chat.subs.merge-gifts' ) ) {
const key = ` ${ e . channel } :ANON ` ;
mystery = mysteries [ key ] = {
recipients : [ ] ,
size : e . massGiftCount ,
expires : Date . now ( ) + 30000
} ;
}
e . body = '' ;
const out = i . convertMessage ( { message : e } ) ;
out . ffz _type = 'sub_mystery' ;
out . sub _anon = true ;
out . mystery = mystery ;
out . sub _plan = e . plan ;
out . sub _count = e . massGiftCount ;
out . sub _total = e . senderCount ;
//t.log.info('Anon Sub Mystery', e, out);
return i . postMessageToCurrentChannel ( e , out ) ;
} catch ( err ) {
t . log . capture ( err , { extra : e } ) ;
return old _anonsubmystery . call ( i , e ) ;
}
}
2018-02-02 16:00:58 -05:00
const old _ritual = this . onRitualEvent ;
this . onRitualEvent = function ( e ) {
try {
const out = i . convertMessage ( e ) ;
out . ffz _type = 'ritual' ;
out . ritual = e . type ;
2018-04-28 17:56:03 -04:00
return i . postMessageToCurrentChannel ( e , out ) ;
2018-02-02 16:00:58 -05:00
} catch ( err ) {
2018-04-11 17:05:31 -04:00
t . log . capture ( err , { extra : e } ) ;
2018-02-02 16:00:58 -05:00
return old _ritual . call ( i , e ) ;
}
}
2019-11-11 14:38:49 -05:00
const old _points = this . onChannelPointsRewardEvent ;
this . onChannelPointsRewardEvent = function ( e ) {
try {
2020-01-22 16:58:55 -05:00
const reward = e . rewardID && get ( e . rewardID , i . props . rewardMap ) ;
if ( reward ) {
const out = i . convertMessage ( e ) ;
2019-11-11 14:38:49 -05:00
2020-01-22 16:58:55 -05:00
out . ffz _type = 'points' ;
out . ffz _reward = reward ;
2019-11-11 14:38:49 -05:00
2020-01-22 16:58:55 -05:00
return i . postMessageToCurrentChannel ( e , out ) ;
2019-11-11 14:38:49 -05:00
}
} catch ( err ) {
t . log . error ( err ) ;
t . log . capture ( err , { extra : e } ) ;
}
return old _points . call ( i , e ) ;
}
2017-12-14 05:43:56 +01:00
const old _host = this . onHostingEvent ;
this . onHostingEvent = function ( e , _t ) {
t . emit ( 'tmi:host' , e , _t ) ;
return old _host . call ( i , e , _t ) ;
}
const old _unhost = this . onUnhostEvent ;
this . onUnhostEvent = function ( e , _t ) {
t . emit ( 'tmi:unhost' , e , _t ) ;
return old _unhost . call ( i , e , _t ) ;
}
2018-08-28 19:13:26 -04:00
const old _add = this . addMessage ;
this . addMessage = function ( e ) {
2018-06-27 14:13:59 -04:00
const original = i . _wrapped ;
2018-04-28 17:56:03 -04:00
if ( original && ! e . _ffz _checked )
2018-06-27 14:13:59 -04:00
return i . postMessageToCurrentChannel ( original , e ) ;
2017-11-13 01:23:39 -05:00
2018-08-28 19:13:26 -04:00
return old _add . call ( i , e ) ;
2017-11-13 01:23:39 -05:00
}
this . _ffz _init = true ;
}
return old _handler . apply ( this , ... args ) ;
}
2018-04-28 17:56:03 -04:00
cls . prototype . postMessageToCurrentChannel = function ( original , message ) {
2019-08-23 18:13:35 -04:00
const original _msg = message ;
2018-04-28 17:56:03 -04:00
message . _ffz _checked = true ;
2019-08-23 18:13:35 -04:00
// For certain message types, the message is contained within
// a message sub-object.
if ( message . type === t . chat _types . ChannelPointsReward )
message = message . message ;
2018-04-28 17:56:03 -04:00
if ( original . channel ) {
let chan = message . channel = original . channel . toLowerCase ( ) ;
if ( chan . startsWith ( '#' ) )
chan = chan . slice ( 1 ) ;
2018-08-28 19:13:26 -04:00
if ( chan !== this . props . channelLogin . toLowerCase ( ) )
2018-04-28 17:56:03 -04:00
return ;
message . roomLogin = chan ;
}
if ( original . message ) {
2019-01-18 19:07:57 -05:00
const user = original . message . user ,
flags = original . message . flags ;
2018-04-28 17:56:03 -04:00
if ( user )
message . emotes = user . emotes ;
2019-10-11 17:41:07 -04:00
if ( flags && this . getFilterFlagOptions ) {
const clear _mod = this . props . isCurrentUserModerator && t . chat . context . get ( 'chat.automod.run-as-mod' ) ;
if ( clear _mod )
this . props . isCurrentUserModerator = false ;
2019-01-18 19:07:57 -05:00
message . flags = this . getFilterFlagOptions ( flags ) ;
2019-10-11 17:41:07 -04:00
if ( clear _mod )
this . props . isCurrentUserModerator = true ;
}
2019-01-18 19:07:57 -05:00
2018-04-28 17:56:03 -04:00
if ( typeof original . action === 'string' )
message . message = original . action ;
else
message . message = original . message . body ;
}
2019-08-23 18:13:35 -04:00
this . addMessage ( original _msg ) ;
2018-04-28 17:56:03 -04:00
}
2017-11-13 01:23:39 -05:00
}
updateChatLines ( ) {
2017-11-14 22:13:30 -05:00
this . chat _line . updateLines ( ) ;
2017-11-13 01:23:39 -05:00
}
// ========================================================================
// Room Handling
// ========================================================================
addRoom ( thing , props ) {
if ( ! props )
props = thing . props ;
if ( ! props . channelID )
return null ;
2017-11-14 18:48:56 -05:00
const room = thing . _ffz _room = this . chat . getRoom ( props . channelID , props . channelLogin && props . channelLogin . toLowerCase ( ) , false , true ) ;
2017-11-13 01:23:39 -05:00
room . ref ( thing ) ;
return room ;
}
removeRoom ( thing ) { // eslint-disable-line class-methods-use-this
if ( ! thing . _ffz _room )
return ;
thing . _ffz _room . unref ( thing ) ;
thing . _ffz _room = null ;
}
// ========================================================================
// Chat Controller
// ========================================================================
chatMounted ( chat , props ) {
2018-07-14 14:13:28 -04:00
if ( chat . chatBuffer )
chat . chatBuffer . ffzController = chat ;
2017-11-13 01:23:39 -05:00
if ( ! props )
props = chat . props ;
if ( ! this . addRoom ( chat , props ) )
return ;
this . updateRoomBitsConfig ( chat , props . bitsConfig ) ;
2018-08-20 14:33:30 -04:00
// TODO: Check if this is the room for the current channel.
this . settings . updateContext ( {
moderator : props . isCurrentUserModerator ,
chatHidden : props . isHidden
} ) ;
2019-06-14 21:24:48 -04:00
if ( props . isEmbedded || props . isPopout )
this . settings . updateContext ( {
channel : props . channelLogin && props . channelLogin . toLowerCase ( ) ,
channelID : props . channelID
} ) ;
2018-08-20 14:33:30 -04:00
this . chat . context . updateContext ( {
moderator : props . isCurrentUserModerator ,
channel : props . channelLogin && props . channelLogin . toLowerCase ( ) ,
channelID : props . channelID ,
2019-06-13 22:56:50 -04:00
/ * u i : {
2018-08-20 14:33:30 -04:00
theme : props . theme
2019-06-13 22:56:50 -04:00
} * /
2018-08-20 14:33:30 -04:00
} ) ;
2017-11-13 01:23:39 -05:00
}
2019-06-14 21:24:48 -04:00
chatUnmounted ( chat ) {
2018-07-14 14:13:28 -04:00
if ( chat . chatBuffer && chat . chatBuffer . ffzController === this )
chat . chatBuffer . ffzController = null ;
2019-06-14 21:24:48 -04:00
if ( chat . props . isEmbedded || chat . props . isPopout )
this . settings . updateContext ( {
channel : null ,
channelID : null
} ) ;
2019-11-11 14:38:49 -05:00
this . settings . updateContext ( {
moderator : false ,
chatHidden : false
} ) ;
2019-06-14 21:24:48 -04:00
this . chat . context . updateContext ( {
moderator : false ,
channel : null ,
channelID : null
} ) ;
2018-07-14 14:13:28 -04:00
this . removeRoom ( chat ) ;
}
2017-11-13 01:23:39 -05:00
chatUpdated ( chat , props ) {
2018-07-14 14:13:28 -04:00
if ( chat . chatBuffer )
chat . chatBuffer . ffzController = chat ;
2019-05-04 03:36:10 -04:00
if ( ! chat . _ffz _room || props . channelID != chat . _ffz _room . id ) {
2017-11-13 01:23:39 -05:00
this . removeRoom ( chat ) ;
2019-05-04 00:21:43 -04:00
if ( chat . _ffz _mounted )
2019-11-11 14:38:49 -05:00
this . chatMounted ( chat ) ;
2017-11-13 01:23:39 -05:00
return ;
}
if ( props . bitsConfig !== chat . props . bitsConfig )
2019-11-11 14:38:49 -05:00
this . updateRoomBitsConfig ( chat , chat . props . bitsConfig ) ;
2017-11-13 01:23:39 -05:00
// TODO: Check if this is the room for the current channel.
2019-11-11 14:38:49 -05:00
let login = chat . props . channelLogin ;
if ( login )
login = login . toLowerCase ( ) ;
if ( chat . props . isEmbedded || chat . props . isPopout )
2019-06-14 21:24:48 -04:00
this . settings . updateContext ( {
2019-11-11 14:38:49 -05:00
channel : login ,
channelID : chat . props . channelID
2019-06-14 21:24:48 -04:00
} ) ;
2017-11-13 01:23:39 -05:00
this . settings . updateContext ( {
2019-11-11 14:38:49 -05:00
moderator : chat . props . isCurrentUserModerator ,
chatHidden : chat . props . isHidden
2017-11-13 01:23:39 -05:00
} ) ;
this . chat . context . updateContext ( {
2019-11-11 14:38:49 -05:00
moderator : chat . props . isCurrentUserModerator ,
channel : login ,
channelID : chat . props . channelID ,
2019-06-13 22:56:50 -04:00
/ * u i : {
2017-11-13 01:23:39 -05:00
theme : props . theme
2019-06-13 22:56:50 -04:00
} * /
2017-11-13 01:23:39 -05:00
} ) ;
}
updateRoomBitsConfig ( chat , config ) { // eslint-disable-line class-methods-use-this
const room = chat . _ffz _room ;
if ( ! room )
return ;
2019-04-30 15:18:29 -04:00
// We have to check that the available cheers haven't changed
// to avoid doing too many recalculations.
let new _bits = null ;
if ( config && Array . isArray ( config . orderedActions ) ) {
new _bits = new Set ;
for ( const action of config . orderedActions )
if ( action && action . prefix )
new _bits . add ( action . prefix ) ;
}
if ( ( ! this . _ffz _old _bits && ! new _bits ) || set _equals ( this . _ffz _old _bits , new _bits ) )
return ;
this . _ffz _old _bits = new _bits ;
2017-11-13 01:23:39 -05:00
room . updateBitsConfig ( formatBitsConfig ( config ) ) ;
this . updateChatLines ( ) ;
}
2018-08-28 19:13:26 -04:00
// ========================================================================
// Chat Buffer Connector
// ========================================================================
connectorMounted ( inst ) { // eslint-disable-line class-methods-use-this
const buffer = inst . props . messageBufferAPI ;
if ( buffer && buffer . _ffz _inst && buffer . _ffz _inst . _ffz _connector !== inst )
buffer . _ffz _inst . _ffz _connector = inst ;
}
connectorUpdated ( inst , props ) { // eslint-disable-line class-methods-use-this
2019-11-11 14:38:49 -05:00
const buffer = props . messageBufferAPI ,
new _buffer = inst . props . messageBufferAPI ;
2018-08-28 19:13:26 -04:00
if ( buffer === new _buffer )
return ;
if ( buffer && buffer . _ffz _inst && buffer . _ffz _inst . _ffz _connector === inst )
buffer . _ffz _inst . _ffz _connector = null ;
if ( new _buffer && new _buffer . _ffz _inst && new _buffer . _ffz _inst . _ffz _connector !== inst )
buffer . _ffz _inst . _ffz _connector = inst ;
}
connectorUnmounted ( inst ) { // eslint-disable-line class-methods-use-this
const buffer = inst . props . messageBufferAPI ;
if ( buffer && buffer . _ffz _inst && buffer . _ffz _inst . _ffz _connector === inst )
buffer . _ffz _inst . _ffz _connector = null ;
}
2017-11-13 01:23:39 -05:00
// ========================================================================
// Chat Containers
// ========================================================================
containerMounted ( cont , props ) {
if ( ! props )
props = cont . props ;
if ( ! this . addRoom ( cont , props ) )
return ;
2019-09-07 14:34:40 -04:00
this . updateRoomBitsConfig ( cont , props . bitsConfig ) ;
2018-01-19 17:17:16 -05:00
if ( props . data ) {
2020-06-30 19:48:46 -04:00
if ( Twilight . POPOUT _ROUTES . includes ( this . router . current _name ) ) {
const color = props . data . user ? . primaryColorHex ;
this . resolve ( 'site.channel' ) . updateChannelColor ( color ) ;
this . settings . updateContext ( {
channel : props . channelLogin ,
channelID : props . channelID ,
channelColor : color
} ) ;
}
2018-01-19 17:17:16 -05:00
this . chat . badges . updateTwitchBadges ( props . data . badges ) ;
this . updateRoomBadges ( cont , props . data . user && props . data . user . broadcastBadges ) ;
2019-04-29 18:14:04 -04:00
this . updateRoomRules ( cont , props . chatRules ) ;
2017-11-13 01:23:39 -05:00
}
}
2020-06-30 19:48:46 -04:00
containerUnmounted ( cont ) {
if ( Twilight . POPOUT _ROUTES . includes ( this . router . current _name ) ) {
this . resolve ( 'site.channel' ) . updateChannelColor ( ) ;
this . settings . updateContext ( {
channel : null ,
channelID : null ,
channelColor : null
} ) ;
}
this . removeRoom ( cont ) ;
}
2017-11-13 01:23:39 -05:00
containerUpdated ( cont , props ) {
2019-05-07 15:04:12 -04:00
// If we don't have a room, or if the room ID doesn't match our ID
// then we need to just create a new Room because the chat room changed.
2019-05-04 03:36:10 -04:00
if ( ! cont . _ffz _room || props . channelID != cont . _ffz _room . id ) {
2017-11-13 01:23:39 -05:00
this . removeRoom ( cont ) ;
2019-05-04 00:21:43 -04:00
if ( cont . _ffz _mounted )
this . containerMounted ( cont , props ) ;
2017-11-13 01:23:39 -05:00
return ;
}
2019-09-07 14:34:40 -04:00
if ( props . bitsConfig !== cont . props . bitsConfig )
this . updateRoomBitsConfig ( cont , props . bitsConfig ) ;
2020-06-30 19:48:46 -04:00
if ( props . data && Twilight . POPOUT _ROUTES . includes ( this . router . current _name ) ) {
const color = props . data . user ? . primaryColorHex ;
this . resolve ( 'site.channel' ) . updateChannelColor ( color ) ;
this . settings . updateContext ( {
channel : props . channelLogin ,
channelID : props . channelID ,
channelColor : color
} ) ;
}
2017-11-13 01:23:39 -05:00
// Twitch, React, and Apollo are the trifecta of terror so we
// can't compare the badgeSets property in any reasonable way.
// Instead, just check the lengths to see if they've changed
// and hope that badge versions will never change separately.
2018-01-19 17:17:16 -05:00
const data = props . data || { } ,
odata = cont . props . data || { } ,
2017-11-13 01:23:39 -05:00
2018-01-19 17:17:16 -05:00
bs = data . badges || [ ] ,
obs = odata . badges || [ ] ,
2017-11-13 01:23:39 -05:00
2018-01-19 17:17:16 -05:00
cs = data . user && data . user . broadcastBadges || [ ] ,
ocs = odata . user && odata . user . broadcastBadges || [ ] ;
2017-11-13 01:23:39 -05:00
2019-05-08 22:47:38 -04:00
if ( this . chat . badges . getTwitchBadgeCount ( ) !== bs . length || bs . length !== obs . length )
2018-01-19 17:17:16 -05:00
this . chat . badges . updateTwitchBadges ( bs ) ;
2017-11-13 01:23:39 -05:00
2019-05-08 22:47:38 -04:00
if ( cont . _ffz _room . badgeCount ( ) !== cs . length || cs . length !== ocs . length )
2018-01-19 17:17:16 -05:00
this . updateRoomBadges ( cont , cs ) ;
2019-04-29 18:14:04 -04:00
this . updateRoomRules ( cont , props . chatRules ) ;
2017-11-13 01:23:39 -05:00
}
2019-05-03 19:30:46 -04:00
hasRoomBadges ( cont ) { // eslint-disable-line class-methods-use-this
const room = cont . _ffz _room ;
if ( ! room )
return false ;
return room . hasBadges ( ) ;
}
2017-11-13 01:23:39 -05:00
updateRoomBadges ( cont , badges ) { // eslint-disable-line class-methods-use-this
const room = cont . _ffz _room ;
if ( ! room )
return ;
room . updateBadges ( badges ) ;
this . updateChatLines ( ) ;
}
2019-04-29 18:14:04 -04:00
updateRoomRules ( cont , rules ) { // eslint-disable-line class-methods-use-this
const room = cont . _ffz _room ;
if ( ! room )
return ;
room . rules = rules ;
}
2017-11-13 01:23:39 -05:00
}
// ============================================================================
// Processing Functions
// ============================================================================
export function formatBitsConfig ( config ) {
if ( ! config )
return ;
const out = { } ,
2020-03-25 19:05:38 -04:00
actions = config . indexedActions ,
tier _colors = { } ;
if ( Array . isArray ( config . tiers ) )
for ( const tier of config . tiers )
tier _colors [ tier . bits ] = tier . color ;
2017-11-13 01:23:39 -05:00
for ( const key in actions )
if ( has ( actions , key ) ) {
const action = actions [ key ] ,
new _act = out [ key ] = {
id : action . id ,
prefix : action . prefix ,
tiers : [ ]
} ;
2020-03-25 19:05:38 -04:00
if ( config ? . getImage ) {
for ( const tier of action . orderedTiers ) {
const images = { } ;
for ( const theme of [ 'light' , 'dark' ] ) {
const themed = images [ theme ] = images [ theme ] || { } ,
stat = themed . static = themed . static || { } ,
animated = themed . animated = themed . animated || { } ;
for ( const scale of [ 1 , 2 , 4 ] ) {
// Static Images
stat [ scale ] = config . getImage ( action . prefix , theme , 'static' , tier . bits , scale , 'png' ) ;
// Animated Images
animated [ scale ] = config . getImage ( action . prefix , theme , 'animated' , tier . bits , scale , 'gif' ) ;
}
}
2017-11-13 01:23:39 -05:00
2020-03-25 19:05:38 -04:00
new _act . tiers . push ( {
amount : tier . bits ,
color : tier . color || tier _colors [ tier . bits ] || 'inherit' ,
id : tier . id ,
images
} ) ;
2017-11-13 01:23:39 -05:00
}
2020-03-25 19:05:38 -04:00
} else if ( action . orderedTiers [ 0 ] ? . images ) {
for ( const tier of action . orderedTiers ) {
const images = { } ;
for ( const im of tier . images ) {
const themed = images [ im . theme ] = images [ im . theme ] || [ ] ,
ak = im . isAnimated ? 'animated' : 'static' ,
anim = themed [ ak ] = themed [ ak ] || { } ;
anim [ im . dpiScale ] = im . url ;
}
new _act . tiers . push ( {
amount : tier . bits ,
color : tier . color || tier _colors [ tier . bits ] || 'inherit' ,
id : tier . id ,
images
} )
}
2017-11-13 01:23:39 -05:00
}
}
return out ;
2020-06-30 19:48:46 -04:00
}