2017-11-14 04:12:10 -05:00
'use strict' ;
// ============================================================================
// Chat Scroller
// ============================================================================
2018-03-14 13:58:04 -04:00
import Twilight from 'site' ;
2017-11-14 04:12:10 -05:00
import Module from 'utilities/module' ;
2019-06-03 19:47:41 -04:00
const SCROLL _EVENTS = [
'touchmove' ,
'scroll' ,
'wheel' ,
'mousewheel' ,
'DOMMouseScroll'
] ;
2017-11-14 04:12:10 -05:00
2019-06-12 21:13:53 -04:00
let last _id = 0 ;
2017-11-14 04:12:10 -05:00
export default class Scroller extends Module {
constructor ( ... args ) {
super ( ... args ) ;
this . inject ( 'settings' ) ;
this . inject ( 'i18n' ) ;
this . inject ( 'chat' ) ;
this . inject ( 'site.fine' ) ;
2018-03-03 16:38:50 -05:00
this . inject ( 'site.web_munch' ) ;
2017-11-14 04:12:10 -05:00
this . ChatScroller = this . fine . define (
'chat-scroller' ,
2019-07-31 17:13:56 -04:00
n => n . saveScrollRef && n . handleScrollEvent && ! n . renderLines ,
2018-03-14 13:58:04 -04:00
Twilight . CHAT _ROUTES
2017-11-14 04:12:10 -05:00
) ;
this . settings . add ( 'chat.scroller.freeze' , {
default : 0 ,
ui : {
2019-06-13 22:56:50 -04:00
path : 'Chat > Behavior >> Scrolling @{"description": "Please note that FrankerFaceZ is dependent on Twitch\'s own scrolling code working correctly. There are bugs with Twitch\'s scrolling code that have existed for more than six months. If you are using Firefox, Edge, or other non-Webkit browsers, expect to have issues."}' ,
2019-06-03 19:47:41 -04:00
title : 'Pause Chat Scrolling' ,
2017-11-14 04:12:10 -05:00
description : 'Automatically stop chat from scrolling when moving the mouse over it or holding a key.' ,
component : 'setting-select-box' ,
data : [
{ value : 0 , title : 'Disabled' } ,
{ value : 1 , title : 'On Hover' } ,
{ value : 2 , title : 'When Ctrl is Held' } ,
{ value : 3 , title : 'When Meta is Held' } ,
{ value : 4 , title : 'When Alt is Held' } ,
{ value : 5 , title : 'When Shift is Held' } ,
{ value : 6 , title : 'Ctrl or Hover' } ,
{ value : 7 , title : 'Meta or Hover' } ,
{ value : 8 , title : 'Alt or Hover' } ,
{ value : 9 , title : 'Shift or Hover' }
]
}
} ) ;
2018-07-03 18:18:39 -04:00
2019-06-03 19:47:41 -04:00
this . settings . add ( 'chat.scroller.freeze-requires-hover' , {
default : true ,
ui : {
path : 'Chat > Behavior >> Scrolling' ,
title : 'Require the mouse to be over chat to freeze with a hotkey.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.scroller.hover-delay' , {
default : 750 ,
ui : {
path : 'Chat > Behavior >> Scrolling' ,
title : 'Hover Timeout' ,
description : 'Chat will only remain frozen due to mouse hovering for this long after the mouse stops moving.' ,
component : 'setting-combo-box' ,
data : [
{ value : 250 , title : '0.25 Seconds' } ,
{ value : 500 , title : '0.50 Seconds' } ,
{ value : 750 , title : '0.75 Seconds' } ,
{ value : 1000 , title : '1 Second' } ,
{ value : 2500 , title : '2.5 Seconds' } ,
{ value : 5000 , title : '5 Seconds' }
]
}
} ) ;
2018-07-03 18:18:39 -04:00
this . settings . add ( 'chat.scroller.smooth-scroll' , {
default : 0 ,
ui : {
2018-07-09 21:35:31 -04:00
path : 'Chat > Behavior >> Scrolling' ,
2018-07-03 18:18:39 -04:00
title : 'Smooth Scrolling' ,
2018-07-03 18:39:00 -04:00
description : 'Smoothly slide new chat messages into view. Speed will increase as necessary to keep up with chat.' ,
2018-07-03 18:18:39 -04:00
component : 'setting-select-box' ,
data : [
{ value : 0 , title : 'Disabled' } ,
{ value : 1 , title : 'Slow' } ,
{ value : 2 , title : 'Medium' } ,
{ value : 3 , title : 'Fast' } ,
{ value : 4 , title : 'Very Fast' }
]
}
} ) ;
2017-11-14 04:12:10 -05:00
}
2019-05-16 14:46:26 -04:00
updateUseKeys ( ) {
const old _use = this . use _keys ;
this . use _keys = false ;
for ( const act of this . chat . context . get ( 'chat.actions.inline' ) )
2019-06-03 19:47:41 -04:00
if ( act && act . display && act . display . keys ) {
2019-05-16 14:46:26 -04:00
this . use _keys = true ;
2019-06-03 19:47:41 -04:00
break ;
}
2019-05-16 14:46:26 -04:00
if ( this . use _keys !== old _use ) {
for ( const inst of this . ChatScroller . instances )
inst && inst . ffzUpdateKeys && inst . ffzUpdateKeys ( ) ;
}
}
2019-06-03 19:47:41 -04:00
async onEnable ( ) {
this . on ( 'i18n:update' , ( ) => this . ChatScroller . forceUpdate ( ) ) ;
this . chat . context . on ( 'changed:chat.actions.inline' , this . updateUseKeys , this ) ;
this . updateUseKeys ( ) ;
this . pause _hover = this . chat . context . get ( 'chat.scroller.freeze-requires-hover' ) ;
this . chat . context . on ( 'changed:chat.scroller.freeze-requires-hover' , val => {
this . pause _hover = val ;
2017-11-14 04:12:10 -05:00
for ( const inst of this . ChatScroller . instances )
2019-06-03 19:47:41 -04:00
inst . ffzMaybeUnpause ( ) ;
} )
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
this . pause _delay = this . chat . context . get ( 'chat.scroller.hover-delay' ) ;
this . chat . context . on ( 'changed:chat.scroller.hover-delay' , val => {
this . pause _delay = val ;
for ( const inst of this . ChatScroller . instances )
inst . ffzMaybeUnpause ( ) ;
} )
this . pause = this . chat . context . get ( 'chat.scroller.freeze' ) ;
2017-11-14 04:12:10 -05:00
this . chat . context . on ( 'changed:chat.scroller.freeze' , val => {
2019-06-03 19:47:41 -04:00
this . pause = val ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
for ( const inst of this . ChatScroller . instances )
inst . ffzMaybeUnpause ( ) ;
2017-11-14 04:12:10 -05:00
} ) ;
2019-06-03 19:47:41 -04:00
this . smooth _scroll = this . chat . context . get ( 'chat.scroller.smooth-scroll' ) ;
2018-07-03 18:18:39 -04:00
this . chat . context . on ( 'changed:chat.scroller.smooth-scroll' , val => {
2019-06-03 19:47:41 -04:00
this . smooth _scroll = val ;
2018-07-03 18:18:39 -04:00
2018-07-03 18:39:00 -04:00
for ( const inst of this . ChatScroller . instances )
2018-07-03 18:18:39 -04:00
inst . ffzSetSmoothScroll ( val ) ;
} ) ;
2019-06-03 19:47:41 -04:00
const t = this ,
React = await this . web _munch . findModule ( 'react' ) ,
createElement = React && React . createElement ;
if ( ! createElement )
return t . log . warn ( ` Unable to get React. ` ) ;
2017-11-14 04:12:10 -05:00
this . ChatScroller . ready ( ( cls , instances ) => {
2019-06-03 19:47:41 -04:00
const old _catch = cls . prototype . componentDidCatch ,
2018-03-03 16:38:50 -05:00
old _render = cls . prototype . render ;
// 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 ,
ffz _total _errors : ( this . state . ffz _total _errors || 0 ) + 1
} ) ;
2018-04-11 17:05:31 -04:00
t . log . capture ( err , { extra : info } ) ;
2018-03-03 16:38:50 -05:00
t . log . info ( 'Error within Chat' , err , info , errs ) ;
}
if ( old _catch )
return old _catch . call ( this , err , info ) ;
}
cls . prototype . ffzZeroErrors = function ( ) {
this . setState ( { ffz _errors : 0 } ) ;
}
cls . prototype . render = function ( ) {
if ( this . state . ffz _errors > 0 ) {
let timer ;
const auto = this . state . ffz _total _errors < 10 ,
handler = ( ) => {
clearTimeout ( timer ) ;
this . ffzZeroErrors ( ) ;
}
if ( auto )
timer = setTimeout ( handler , 250 ) ;
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
} , [
2018-04-01 18:24:08 -04:00
createElement ( 'div' , { className : 'tw-mg-b-1' } , 'There was an error displaying chat.' ) ,
! auto && createElement ( 'button' , {
2018-03-03 16:38:50 -05:00
className : 'tw-button' ,
onClick : handler
2018-04-01 18:24:08 -04:00
} , createElement ( 'span' , { className : 'tw-button__text' } , 'Try Again' ) )
2018-03-03 16:38:50 -05:00
] ) ;
} else
return old _render . call ( this ) ;
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzInstallHandler = function ( ) {
if ( this . _ffz _installed )
2017-11-14 04:12:10 -05:00
return ;
2019-06-03 19:47:41 -04:00
this . _ffz _installed = true ;
const inst = this ;
2019-06-13 22:56:50 -04:00
inst . ffz _outside = true ;
inst . _ffz _accessor = ` _ffz_contains_ ${ last _id ++ } ` ;
2019-06-12 21:13:53 -04:00
t . on ( 'tooltips:mousemove' , this . ffzTooltipHover , this ) ;
t . on ( 'tooltips:leave' , this . ffzTooltipLeave , this ) ;
2019-06-03 19:47:41 -04:00
inst . ffz _oldScrollEvent = inst . handleScrollEvent ;
inst . ffz _oldScroll = inst . scrollToBottom ;
// New Scroll to Bottom
inst . ffz _doScroll = function ( ) {
inst . _ffz _scroll _frame = null ;
if ( inst . state . isAutoScrolling && ! inst . state . isPaused ) {
if ( inst . ffz _smooth _scroll && ! inst . _ffz _one _fast _scroll )
inst . smoothScrollBottom ( ) ;
else {
inst . _ffz _one _fast _scroll = false ;
inst . ffz _oldScroll ( ) ;
}
}
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
inst . scrollToBottom = function ( ) {
if ( inst . _ffz _scroll _frame || inst . state . isPaused )
2017-11-14 04:12:10 -05:00
return ;
2019-06-03 19:47:41 -04:00
this . _ffz _scroll _frame = requestAnimationFrame ( inst . ffz _doScroll ) ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
// New Scroll Event Handling
inst . handleScrollEvent = function ( event ) {
if ( ! inst . scroll || ! inst . scroll . scrollContent )
return ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
// TODO: Check for mousedown?
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
if ( ! ( event . which > 0 || event . type === 'mousewheel' || event . type === 'wheel' || event . type === 'touchmove' ) )
return ;
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
// How far are we scrolled up?
const scroller = inst . scroll . scrollContent ,
offset = scroller . scrollHeight - scroller . scrollTop - scroller . offsetHeight ;
// If we're less than 10 pixels from the bottom and we aren't autoscrolling, resume
if ( offset <= 10 && ! inst . state . isAutoScrolling )
inst . resume ( ) ;
// If we are autoscrolling and we're more than 10 pixels up, then
// stop autoscrolling without setting paused.
else if ( inst . state . isAutoScrolling && offset > 10 ) {
// If we're paused, unpause.
if ( inst . state . isPaused ) {
inst . setState ( {
isPaused : false
} , ( ) => {
if ( inst . props . setPaused )
inst . props . setPaused ( false ) ;
} ) ;
inst . setLoadMoreEnabled ( true ) ;
}
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
inst . setState ( {
isAutoScrolling : false
} ) ;
}
}
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
inst . pause = function ( ) {
// If we already aren't scrolling, we don't want to further
// pause things.
if ( ! inst . state . isAutoScrolling )
return ;
2018-07-13 14:32:12 -04:00
2019-06-03 19:47:41 -04:00
inst . setState ( {
isPaused : true
} , ( ) => {
if ( inst . props . setPaused )
inst . props . setPaused ( true ) ;
} ) ;
}
2018-07-03 18:18:39 -04:00
2019-06-03 19:47:41 -04:00
const old _resume = inst . resume ;
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
inst . ffzFastResume = function ( ) {
inst . _ffz _one _fast _scroll = true ;
inst . resume ( ) ;
2018-07-03 18:18:39 -04:00
}
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
inst . resume = function ( ) {
clearInterval ( inst . _ffz _hover _timer ) ;
inst . _ffz _hover _timer = null ;
old _resume . call ( inst ) ;
}
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
// Event Registration
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
const Mousetrap = t . web _munch . getModule ( 'mousetrap' ) || window . Mousetrap ;
if ( Mousetrap != null ) {
Mousetrap . unbind ( 'alt' , 'keydown' ) ;
Mousetrap . unbind ( 'alt' , 'keyup' ) ;
}
2018-07-03 18:39:00 -04:00
2019-06-03 19:47:41 -04:00
inst . ffzHandleKey = inst . ffzHandleKey . bind ( inst ) ;
2019-06-08 17:35:48 -04:00
window . addEventListener ( 'keydown' , inst . ffzHandleKey ) ;
window . addEventListener ( 'keyup' , inst . ffzHandleKey ) ;
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
inst . hoverPause = inst . ffzMouseMove . bind ( inst ) ;
inst . hoverResume = inst . ffzMouseLeave . bind ( inst ) ;
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
const node = t . fine . getChildNode ( inst ) ;
if ( node )
node . addEventListener ( 'mousemove' , inst . hoverPause ) ;
2019-04-30 15:18:29 -04:00
2019-06-03 19:47:41 -04:00
const scroller = this . scroll && this . scroll . scrollContent ;
if ( scroller ) {
for ( const event of SCROLL _EVENTS ) {
scroller . removeEventListener ( event , inst . ffz _oldScrollEvent ) ;
scroller . addEventListener ( event , inst . handleScrollEvent ) ;
2018-07-03 18:18:39 -04:00
}
2018-01-15 20:40:54 -05:00
}
2019-06-03 19:47:41 -04:00
// We need to refresh the element to make sure it's using the correct
// event handlers for mouse enter / leave.
inst . forceUpdate ( ) ;
}
2019-04-30 15:18:29 -04:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzSetSmoothScroll = function ( value ) {
this . ffz _smooth _scroll = value ;
this . ffzMaybeUnpause ( ) ;
}
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
// Event Handling
2017-11-22 15:39:38 -05:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzReadKeysFromEvent = function ( event ) {
if ( event . altKey === this . ffz _alt &&
event . shiftKey === this . ffz _shift &&
event . ctrlKey === this . ffz _ctrl &&
event . metaKey === this . ffz _meta )
return false ;
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
this . ffz _alt = event . altKey ;
this . ffz _shift = event . shiftKey ;
this . ffz _ctrl = event . ctrlKey ;
this . ffz _meta = event . metaKey ;
return true ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
cls . prototype . ffzHandleKey = function ( event ) {
if ( ! this . ffzReadKeysFromEvent ( event ) )
2017-11-14 04:12:10 -05:00
return ;
2019-06-03 19:47:41 -04:00
this . ffzUpdateKeyTags ( ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
if ( ( t . pause _hover && this . ffz _outside ) || t . pause < 2 )
return ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
const should _pause = this . ffzShouldBePaused ( ) ,
changed = should _pause !== this . state . isPaused ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
if ( changed )
if ( should _pause ) {
this . pause ( ) ;
this . setLoadMoreEnabled ( false ) ;
} else
this . resume ( ) ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
cls . prototype . ffzInstallHoverTimer = function ( ) {
if ( this . _ffz _hover _timer )
return ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
this . _ffz _hover _timer = setInterval ( ( ) => {
if ( this . state . isPaused && this . ffzShouldBePaused ( ) )
return ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
this . ffzMaybeUnpause ( ) ;
} , 50 ) ;
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzMouseMove = function ( event ) {
this . ffz _last _move = Date . now ( ) ;
const was _outside = this . ffz _outside ;
this . ffz _outside = false ;
2017-11-16 15:54:58 -05:00
2019-06-03 19:47:41 -04:00
if ( this . _ffz _outside _timer ) {
clearTimeout ( this . _ffz _outside _timer ) ;
this . _ffz _outside _timer = null ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
const keys _updated = this . ffzReadKeysFromEvent ( event ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
// If nothing changed, stop processing.
if ( ! keys _updated && event . screenX === this . ffz _sx && event . screenY === this . ffz _sy ) {
if ( was _outside )
this . ffzUpdateKeyTags ( ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
return ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
this . ffz _sx = event . screenX ;
this . ffz _sy = event . screenY ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
if ( keys _updated || was _outside )
this . ffzUpdateKeyTags ( ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
const should _pause = this . ffzShouldBePaused ( ) ,
changed = should _pause !== this . state . isPaused ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
if ( changed )
if ( should _pause ) {
this . pause ( ) ;
this . ffzInstallHoverTimer ( ) ;
this . setLoadMoreEnabled ( false ) ;
2019-05-16 14:46:26 -04:00
2019-06-03 19:47:41 -04:00
} else
this . resume ( ) ;
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzMouseLeave = function ( ) {
this . ffz _outside = true ;
if ( this . _ffz _outside _timer )
clearTimeout ( this . _ffz _outside _timer ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
this . _ffz _outside _timer = setTimeout ( ( ) => this . ffzMaybeUnpause ( ) , 64 ) ;
this . ffzUpdateKeyTags ( ) ;
2017-11-14 04:12:10 -05:00
}
2019-06-12 21:13:53 -04:00
cls . prototype . ffzTooltipHover = function ( target , tip , event ) {
if ( target [ this . _ffz _accessor ] == null ) {
const scroller = this . scroll && this . scroll . scrollContent ;
target [ this . _ffz _accessor ] = scroller ? scroller . contains ( target ) : false ;
}
if ( target [ this . _ffz _accessor ] )
this . ffzMouseMove ( event ) ;
}
cls . prototype . ffzTooltipLeave = function ( target ) {
if ( this . ffz _outside )
return ;
if ( target [ this . _ffz _accessor ] == null ) {
const scroller = this . scroll && this . scroll . scrollContent ;
target [ this . _ffz _accessor ] = scroller ? scroller . contains ( target ) : false ;
}
if ( target [ this . _ffz _accessor ] )
this . ffzMouseLeave ( ) ;
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
// Keyboard Stuff
cls . prototype . ffzUpdateKeyTags = function ( ) {
if ( ! this . _ffz _key _frame )
this . _ffz _key _frame = requestAnimationFrame ( ( ) => this . ffz _updateKeyTags ( ) ) ;
2019-05-16 14:46:26 -04:00
}
2019-06-03 19:47:41 -04:00
cls . prototype . ffz _updateKeyTags = function ( ) {
this . _ffz _key _frame = null ;
2019-05-16 14:46:26 -04:00
if ( ! t . use _keys && this . ffz _use _keys === t . use _keys )
return ;
if ( ! this . scroll || ! this . scroll . root )
return ;
this . ffz _use _keys = t . use _keys ;
this . scroll . root . classList . toggle ( 'ffz--keys' , t . use _keys ) ;
const ds = this . scroll . root . dataset ;
if ( ! t . use _keys ) {
delete ds . alt ;
delete ds . ctrl ;
delete ds . shift ;
delete ds . meta ;
} else {
ds . alt = ! this . ffz _outside && this . ffz _alt ;
ds . ctrl = ! this . ffz _outside && this . ffz _ctrl ;
ds . shift = ! this . ffz _outside && this . ffz _shift ;
ds . meta = ! this . ffz _outside && this . ffz _meta ;
}
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
// Pause Stuff
2019-05-16 14:46:26 -04:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzShouldBePaused = function ( since ) {
if ( since == null )
since = Date . now ( ) - this . ffz _last _move ;
2019-05-16 14:46:26 -04:00
2019-06-03 19:47:41 -04:00
const mode = t . pause ,
require _hover = t . pause _hover ;
return ( ! require _hover || ! this . ffz _outside ) && this . state . isAutoScrolling && (
( this . ffz _ctrl && ( mode === 2 || mode === 6 ) ) ||
( this . ffz _meta && ( mode === 3 || mode === 7 ) ) ||
( this . ffz _alt && ( mode === 4 || mode === 8 ) ) ||
( this . ffz _shift && ( mode === 5 || mode === 9 ) ) ||
( ! this . ffz _outside && since < t . pause _delay && ( mode === 1 || mode > 5 ) )
) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
}
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
cls . prototype . ffzMaybeUnpause = function ( ) {
if ( this . state . isPaused && ! this . _ffz _unpause _frame )
this . _ffz _unpause _frame = requestAnimationFrame ( ( ) => {
this . _ffz _unpause _frame = null ;
if ( this . state . isPaused && ! this . ffzShouldBePaused ( ) )
this . resume ( ) ;
} ) ;
}
2019-05-16 14:46:26 -04:00
2019-06-03 19:47:41 -04:00
cls . prototype . listFooter = function ( ) {
2019-06-12 21:13:53 -04:00
let msg , cls = '' ;
2019-06-03 19:47:41 -04:00
if ( this . state . isPaused ) {
const f = t . pause ,
reason = f === 2 ? t . i18n . t ( 'key.ctrl' , 'Ctrl Key' ) :
f === 3 ? t . i18n . t ( 'key.meta' , 'Meta Key' ) :
f === 4 ? t . i18n . t ( 'key.alt' , 'Alt Key' ) :
f === 5 ? t . i18n . t ( 'key.shift' , 'Shift Key' ) :
f === 6 ? t . i18n . t ( 'key.ctrl_mouse' , 'Ctrl or Mouse' ) :
f === 7 ? t . i18n . t ( 'key.meta_mouse' , 'Meta or Mouse' ) :
f === 8 ? t . i18n . t ( 'key.alt_mouse' , 'Alt or Mouse' ) :
f === 9 ? t . i18n . t ( 'key.shift_mouse' , 'Shift or Mouse' ) :
t . i18n . t ( 'key.mouse' , 'Mouse Movement' ) ;
msg = t . i18n . t ( 'chat.paused' , '(Chat Paused Due to {reason})' , { reason } ) ;
2019-06-12 21:13:53 -04:00
cls = 'ffz--freeze-indicator' ;
2019-06-03 19:47:41 -04:00
} else if ( this . state . isAutoScrolling )
return null ;
else
msg = t . i18n . t ( 'chat.messages-below' , 'More messages below.' ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
return createElement ( 'div' , {
2019-06-12 21:13:53 -04:00
className : ` chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-full-width tw-justify-content-center tw-pd-05 ${ cls } ` ,
2019-06-03 19:47:41 -04:00
onClick : this . ffzFastResume
} , createElement ( 'div' , null , msg ) ) ;
2017-11-14 04:12:10 -05:00
}
2019-06-03 19:47:41 -04:00
cls . prototype . smoothScrollBottom = function ( ) {
if ( this . _ffz _smooth _animation )
cancelAnimationFrame ( this . _ffz _smooth _animation ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
this . ffz _is _smooth _scrolling = true ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
// Step setting value is # pixels to scroll per 10ms.
// 1 is pretty slow, 2 medium, 3 fast, 4 very fast.
let step = this . ffz _smooth _scroll ,
old _time = Date . now ( ) ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
const scroll _content = this . scroll . scrollContent ;
if ( ! scroll _content )
return ;
2017-11-14 04:12:10 -05:00
2019-06-03 19:47:41 -04:00
const target _top = scroll _content . scrollHeight - scroll _content . clientHeight ,
difference = target _top - scroll _content . scrollTop ;
// If we are falling behind speed us up
if ( difference > scroll _content . clientHeight ) {
// we are a full scroll away, just jump there
step = difference ;
} else if ( difference > 200 ) {
// we are starting to fall behind, speed it up a bit
step += step * Math . floor ( difference / 200 ) ;
}
const smoothAnimation = ( ) => {
if ( this . state . isPaused || ! this . state . isAutoScrolling )
return this . ffz _is _smooth _scrolling = false ;
// See how much time has passed to get a step based off the delta
const current _time = Date . now ( ) ,
delta = current _time - old _time ,
current _step = step * ( delta / 10 ) ;
// we need to move at least one full pixel for scrollTop to do anything in this delta.
if ( current _step >= 1 ) {
const scroll _top = scroll _content . scrollTop ,
target _top = scroll _content . scrollHeight - scroll _content . clientHeight ;
old _time = current _time ;
if ( scroll _top < target _top ) {
scroll _content . scrollTop = scroll _top + current _step ;
this . _ffz _smooth _animation = requestAnimationFrame ( smoothAnimation ) ;
} else {
// We've reached the bottom.
scroll _content . scrollTop = target _top ;
this . ffz _is _smooth _scrolling = false ;
}
} else {
// The frame happened so quick since last update that we haven't moved a full pixel.
// Just wait.
this . _ffz _smooth _animation = requestAnimationFrame ( smoothAnimation ) ;
}
}
smoothAnimation ( ) ;
2018-07-03 18:18:39 -04:00
}
2019-06-03 19:47:41 -04:00
// Do the thing~
2018-07-03 18:18:39 -04:00
2017-11-14 04:12:10 -05:00
for ( const inst of instances )
this . onMount ( inst ) ;
} ) ;
this . ChatScroller . on ( 'mount' , this . onMount , this ) ;
this . ChatScroller . on ( 'unmount' , this . onUnmount , this ) ;
}
onMount ( inst ) {
2019-06-03 19:47:41 -04:00
inst . ffzSetSmoothScroll ( this . smooth _scroll ) ;
2017-11-16 15:54:58 -05:00
inst . ffzInstallHandler ( ) ;
2017-11-14 04:12:10 -05:00
}
2019-06-08 17:35:48 -04:00
onUnmount ( inst ) { // eslint-disable-line class-methods-use-this
2019-06-12 21:13:53 -04:00
this . off ( 'tooltips:mousemove' , inst . ffzTooltipHover , inst ) ;
this . off ( 'tooltips:leave' , inst . ffzTooltipLeave , inst ) ;
2019-06-08 17:35:48 -04:00
window . removeEventListener ( 'keydown' , inst . ffzHandleKey ) ;
window . removeEventListener ( 'keyup' , inst . ffzHandleKey ) ;
2017-11-14 04:12:10 -05:00
}
}