2019-03-19 15:03:43 -04:00
'use strict' ;
2020-11-14 21:58:22 -05:00
import { IS _FIREFOX } from 'src/utilities/constants' ;
2019-03-19 15:03:43 -04:00
// ============================================================================
// Layout Overrides for Twitch Twilight
// ============================================================================
import Module from 'utilities/module' ;
2020-12-04 14:32:26 -05:00
import { debounce , has } from 'utilities/object' ;
2019-03-19 15:03:43 -04:00
const PORTRAIT _ROUTES = [ 'user' , 'video' , 'user-video' , 'user-clip' , 'user-videos' , 'user-clips' , 'user-collections' , 'user-events' , 'user-followers' , 'user-following' ] ;
2019-08-09 14:24:26 -04:00
const MINIMAL _ROUTES = [ 'popout' , 'embed-chat' , 'dash-chat' ] ;
2019-03-19 15:03:43 -04:00
export default class Layout extends Module {
constructor ( ... args ) {
super ( ... args ) ;
this . should _enable = true ;
this . inject ( 'settings' ) ;
this . inject ( 'site.fine' ) ;
this . inject ( 'site.css_tweaks' ) ;
2020-12-04 14:32:26 -05:00
this . inject ( 'site.elemental' ) ;
2019-03-19 15:03:43 -04:00
2019-11-14 19:52:35 -05:00
/ * t h i s . T o p N a v = t h i s . f i n e . d e f i n e (
2019-09-28 23:52:50 -04:00
'top-nav' ,
n => n . computeStyles && n . navigationLinkSize
2019-11-14 19:52:35 -05:00
) ; * /
2019-09-28 23:52:50 -04:00
2019-10-28 01:06:02 -04:00
/ * t h i s . R i g h t C o l u m n = t h i s . f i n e . d e f i n e (
2019-03-19 15:03:43 -04:00
'tw-rightcolumn' ,
2019-04-18 03:16:19 -04:00
n => n . hideOnBreakpoint && n . handleToggleVisibility
2019-10-28 01:06:02 -04:00
) ; * /
2019-06-06 16:33:14 -04:00
2020-10-16 15:19:11 -04:00
this . ResizeDetector = this . fine . define (
'resize-detector' ,
n => n . maybeDebounceOnScroll && n . setGrowDivRef && n . props . onResize
) ;
2020-12-04 14:32:26 -05:00
this . SideBar = this . elemental . define (
'sidebar' ,
'.side-bar-contents' ,
null ,
{ childNodes : true , subtree : true } , 1
) ;
/ * t h i s . S i d e B a r C h a n n e l s = t h i s . f i n e . d e f i n e (
2019-10-23 17:49:01 -05:00
'nav-cards' ,
t => t . getCardSlideInContent && t . props && has ( t . props , 'tooltipContent' )
2020-12-04 14:32:26 -05:00
) ; * /
2019-10-23 17:49:01 -05:00
2021-03-02 16:55:25 -05:00
this . settings . add ( 'clips.layout.big' , {
default : false ,
ui : {
path : 'Appearance > Layout >> Clips' ,
title : 'Allow the player to get larger on `clips.twitch.tv` pages.' ,
description : 'This only affects windows at least 1,200 pixels wide and attempts to make the player and chat replay as large as possible.' ,
component : 'setting-check-box'
}
} ) ;
2019-03-19 15:03:43 -04:00
this . settings . add ( 'layout.portrait' , {
default : false ,
ui : {
path : 'Appearance > Layout >> Channel' ,
title : 'Enable Portrait Mode' ,
description : 'In Portrait Mode, chat will be displayed beneath the player when the window is taller than it is wide.' ,
component : 'setting-check-box'
}
} ) ;
2019-06-17 15:32:38 -04:00
this . settings . add ( 'layout.portrait-invert' , {
default : false ,
ui : {
path : 'Appearance > Layout >> Channel' ,
title : 'When in portrait mode, place chat at the top.' ,
component : 'setting-check-box'
} ,
changed : val => document . body . classList . toggle ( 'ffz--portrait-invert' , val )
} ) ;
2019-03-19 15:03:43 -04:00
this . settings . add ( 'layout.portrait-threshold' , {
default : 1.25 ,
ui : {
path : 'Appearance > Layout >> Channel' ,
title : 'Portrait Mode Threshold' ,
description : 'This is the Width to Height ratio at which point Portrait Mode will begin to activate.' ,
component : 'setting-text-box' ,
process ( val ) {
val = parseFloat ( val , 10 )
if ( isNaN ( val ) || ! isFinite ( val ) || val <= 0 )
return 1.25 ;
return val ;
}
}
2020-11-24 16:43:51 -05:00
} ) ;
this . settings . add ( 'layout.portrait-min-chat' , {
default : false ,
requires : [ 'layout.use-portrait' ] ,
process ( ctx , val ) {
if ( ! ctx . get ( 'layout.use-portrait' ) )
return false ;
return val ;
} ,
ui : {
path : 'Appearance > Layout >> Channel' ,
title : 'Use compact chat in Portrait Mode.' ,
description : 'When enabled, this minimizes the chat header and places the chat input box in line with the chat buttons in order to present a more compact chat able to display more lines with limited vertical space.' ,
component : 'setting-check-box'
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.toggle('portrait-chat', val)
2019-03-19 15:03:43 -04:00
} )
this . settings . add ( 'layout.use-portrait' , {
2020-09-29 14:15:43 -04:00
requires : [ 'layout.portrait' , 'layout.portrait-threshold' , 'context.route.name' , 'context.size' , 'context.isWatchParty' ] ,
2019-03-19 15:03:43 -04:00
process ( ctx ) {
2020-09-29 14:15:43 -04:00
if ( ctx . get ( 'context.isWatchParty' ) )
return false ;
2019-03-19 15:03:43 -04:00
const size = ctx . get ( 'context.size' ) ;
if ( ! size || ! ctx . get ( 'layout.portrait' ) || ! PORTRAIT _ROUTES . includes ( ctx . get ( 'context.route.name' ) ) )
return false ;
const ratio = size . width / size . height ;
return ratio <= ctx . get ( 'layout.portrait-threshold' ) ;
} ,
2019-05-16 14:46:26 -04:00
changed : ( ) => this . updatePortraitMode ( )
2019-03-19 15:03:43 -04:00
} ) ;
2019-04-05 18:04:14 -04:00
this . settings . add ( 'layout.inject-portrait' , {
requires : [ 'layout.use-portrait' , 'context.ui.rightColumnExpanded' ] ,
process ( ctx ) {
return ctx . get ( 'layout.use-portrait' ) && ctx . get ( 'context.ui.rightColumnExpanded' ) ;
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.toggle('portrait', val)
2019-04-05 18:04:14 -04:00
} ) ;
this . settings . add ( 'layout.use-portrait-swapped' , {
requires : [ 'layout.inject-portrait' , 'layout.swap-sidebars' ] ,
process ( ctx ) {
return ctx . get ( 'layout.inject-portrait' ) && ctx . get ( 'layout.swap-sidebars' )
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.toggle('portrait-swapped', val)
2019-04-05 18:04:14 -04:00
} ) ;
2019-11-25 17:50:20 -05:00
this . settings . add ( 'layout.use-portrait-meta' , {
requires : [ 'layout.inject-portrait' , 'player.theatre.metadata' ] ,
process ( ctx ) {
return ctx . get ( 'layout.inject-portrait' ) && ctx . get ( 'player.theatre.metadata' )
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.toggle('portrait-metadata', val)
2019-11-25 17:50:20 -05:00
} ) ;
this . settings . add ( 'layout.use-portrait-meta-top' , {
requires : [ 'layout.use-portrait-meta' , 'layout.portrait-invert' ] ,
process ( ctx ) {
return ctx . get ( 'layout.use-portrait-meta' ) && ! ctx . get ( 'layout.portrait-invert' )
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.toggle('portrait-metadata-top', val)
2019-11-25 17:50:20 -05:00
} ) ;
2023-09-13 16:08:10 -04:00
this . settings . add ( 'layout.is-theater-mode' , {
requires : [ 'context.ui.theatreModeEnabled' , 'context.fullscreen' ] ,
process ( ctx ) {
if ( ctx . get ( 'context.fullscreen' ) )
return false ;
return ctx . get ( 'context.ui.theatreModeEnabled' ) ;
}
} ) ;
2019-03-19 15:03:43 -04:00
this . settings . add ( 'layout.show-portrait-chat' , {
requires : [ 'layout.use-portrait' , 'layout.portrait-extra-height' , 'layout.portrait-extra-width' ] ,
process ( ) {
// TODO: Calculate this based on the expected player height.
return true ;
} ,
changed : ( ) => this . updatePortraitMode ( )
} ) ;
this . settings . add ( 'layout.portrait-extra-height' , {
2023-09-13 16:08:10 -04:00
requires : [ 'context.new_channel' , 'context.squad_bar' , /*'context.hosting',*/ 'layout.is-theater-mode' , 'player.theatre.no-whispers' , 'whispers.show' , 'layout.minimal-navigation' ] ,
2019-03-19 15:03:43 -04:00
process ( ctx ) {
let height = 0 ;
2023-09-13 16:08:10 -04:00
if ( ctx . get ( 'layout.is-theater-mode' ) ) {
2019-03-19 15:03:43 -04:00
if ( ctx . get ( 'layout.minimal-navigation' ) )
height += 1 ;
if ( ctx . get ( 'whispers.show' ) && ! ctx . get ( 'player.theatre.no-whispers' ) )
height += 4 ;
} else {
height = ctx . get ( 'layout.minimal-navigation' ) ? 1 : 5 ;
if ( ctx . get ( 'whispers.show' ) )
height += 4 ;
2019-05-07 15:04:12 -04:00
if ( ctx . get ( 'context.squad_bar' ) )
height += 6 ;
2019-03-19 15:03:43 -04:00
height += ctx . get ( 'context.new_channel' ) ? 1 : 5 ;
2022-10-07 15:12:15 -04:00
/ * i f ( c t x . g e t ( ' c o n t e x t . h o s t i n g ' ) )
height += 4 ; * /
2019-03-19 15:03:43 -04:00
}
return height ;
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.setVariable('portrait-extra-height', `${val}rem`)
2019-03-19 15:03:43 -04:00
} )
this . settings . add ( 'layout.portrait-extra-width' , {
2023-09-13 16:08:10 -04:00
require : [ 'layout.side-nav.show' , 'layout.is-theater-mode' , 'context.ui.sideNavExpanded' ] ,
2019-03-19 15:03:43 -04:00
process ( ctx ) {
2023-09-13 16:08:10 -04:00
if ( ! ctx . get ( 'layout.side-nav.show' ) || ctx . get ( 'layout.is-theater-mode' ) )
2019-03-19 15:03:43 -04:00
return 0 ;
return ctx . get ( 'context.ui.sideNavExpanded' ) ? 24 : 5
} ,
2023-11-05 14:49:39 -05:00
//changed: val => this.css_tweaks.setVariable('portrait-extra-width', `${val}rem`)
2019-03-19 15:03:43 -04:00
} ) ;
2019-04-18 03:16:19 -04:00
this . settings . add ( 'layout.is-minimal' , {
require : [ 'context.route.name' ] ,
process ( ctx ) {
return MINIMAL _ROUTES . includes ( ctx . get ( 'context.route.name' ) ) ;
}
} ) ;
2019-03-19 15:03:43 -04:00
}
onEnable ( ) {
2019-06-17 15:32:38 -04:00
document . body . classList . toggle ( 'ffz--portrait-invert' , this . settings . get ( 'layout.portrait-invert' ) ) ;
2019-09-28 23:52:50 -04:00
this . on ( ':update-nav' , this . updateNavLinks , this ) ;
2020-10-16 15:19:11 -04:00
this . on ( ':resize' , this . handleResize , this ) ;
2019-09-28 23:52:50 -04:00
2023-11-05 14:49:39 -05:00
this . settings . getChanges ( 'layout.portrait-min-chat' , val => this . css _tweaks . toggle ( 'portrait-chat' , val ) ) ;
this . settings . getChanges ( 'layout.inject-portrait' , val => this . css _tweaks . toggle ( 'portrait' , val ) ) ;
this . settings . getChanges ( 'layout.use-portrait-swapped' , val => this . css _tweaks . toggle ( 'portrait-swapped' , val ) ) ;
this . settings . getChanges ( 'layout.use-portrait-meta' , val => this . css _tweaks . toggle ( 'portrait-metadata' , val ) ) ;
this . settings . getChanges ( 'layout.use-portrait-meta-top' , val => this . css _tweaks . toggle ( 'portrait-metadata-top' , val ) ) ;
this . settings . getChanges ( 'layout.portrait-extra-width' , val => this . css _tweaks . setVariable ( 'portrait-extra-width' , ` ${ val } rem ` ) ) ;
this . settings . getChanges ( 'layout.portrait-extra-height' , val => this . css _tweaks . setVariable ( 'portrait-extra-height' , ` ${ val } rem ` ) ) ;
/ * t h i s . c s s _ t w e a k s . t o g g l e ( ' p o r t r a i t - c h a t ' , t h i s . s e t t i n g s . g e t ( ' l a y o u t . p o r t r a i t - m i n - c h a t ' ) ) ;
2019-04-05 18:04:14 -04:00
this . css _tweaks . toggle ( 'portrait' , this . settings . get ( 'layout.inject-portrait' ) ) ;
this . css _tweaks . toggle ( 'portrait-swapped' , this . settings . get ( 'layout.use-portrait-swapped' ) ) ;
2019-11-25 17:50:20 -05:00
this . css _tweaks . toggle ( 'portrait-metadata' , this . settings . get ( 'layout.use-portrait-meta' ) ) ;
this . css _tweaks . toggle ( 'portrait-metadata-top' , this . settings . get ( 'layout.use-portrait-meta-top' ) ) ;
2019-03-19 15:03:43 -04:00
this . css _tweaks . setVariable ( 'portrait-extra-width' , ` ${ this . settings . get ( 'layout.portrait-extra-width' ) } rem ` ) ;
2023-11-05 14:49:39 -05:00
this . css _tweaks . setVariable ( 'portrait-extra-height' , ` ${ this . settings . get ( 'layout.portrait-extra-height' ) } rem ` ) ; * /
2019-03-19 15:03:43 -04:00
2020-07-16 16:34:03 -04:00
this . on ( 'site.directory:update-cards' , ( ) => {
2020-12-04 14:32:26 -05:00
this . SideBar . each ( el => this . _updateSidebar ( el ) ) ;
//for(const inst of this.SideBarChannels.instances)
// this.updateCardClass(inst);
2020-07-16 16:34:03 -04:00
} ) ;
2020-11-14 14:08:27 -05:00
this . ResizeDetector . ready ( ( ) => {
if ( this . _needs _resize ) {
this . _needs _resize = false ;
this . handleResize ( ) ;
}
} ) ;
2020-12-04 14:32:26 -05:00
this . SideBar . on ( 'mount' , this . updateSidebar , this ) ;
this . SideBar . on ( 'mutate' , this . updateSidebar , this ) ;
this . SideBar . each ( el => this . updateSidebar ( el ) ) ;
/ * t h i s . S i d e B a r C h a n n e l s . r e a d y ( ( c l s , i n s t a n c e s ) = > {
2019-10-23 17:49:01 -05:00
for ( const inst of instances )
this . updateCardClass ( inst ) ;
} ) ;
this . SideBarChannels . on ( 'mount' , this . updateCardClass , this ) ;
2020-12-04 14:32:26 -05:00
this . SideBarChannels . on ( 'update' , this . updateCardClass , this ) ; * /
2019-10-23 17:49:01 -05:00
2019-10-28 01:06:02 -04:00
/ * c o n s t t = t h i s ;
2019-03-19 15:03:43 -04:00
this . RightColumn . ready ( ( cls , instances ) => {
cls . prototype . ffzHideOnBreakpoint = function ( ) {
try {
if ( ! this . containerRef )
return ;
if ( ! t . settings . get ( 'layout.use-portrait' ) )
return this . oldHideOnBreakpoint ? this . oldHideOnBreakpoint ( ) : null ;
const should _show = t . settings . get ( 'layout.show-portrait-chat' ) ,
is _showing = this . props . rightColumnExpandedByBreakpoint ;
if ( should _show && ! is _showing ) {
this . containerRef . style . display = '' ;
this . props . expandRightColumnFromBreakpoint ( ) ;
} else if ( ! should _show && is _showing ) {
this . containerRef . style . display = 'none' ;
this . props . collapseRightColumnFromBreakpoint ( ) ;
}
} catch ( err ) {
if ( this . oldHideOnBreakpoint )
this . oldHideOnBreakpoint ( ) ;
t . log . capture ( err ) ;
}
}
cls . prototype . ffzInstall = function ( ) {
if ( this . oldHideOnBreakpoint )
return ;
this . oldHideOnBreakpoint = this . hideOnBreakpoint ;
this . hideOnBreakpoint = ( ) => this . ffzHideOnBreakpoint ( ) ;
}
for ( const inst of instances ) {
window . removeEventListener ( 'resize' , inst . hideOnBreakpoint ) ;
inst . ffzInstall ( ) ;
window . addEventListener ( 'resize' , inst . hideOnBreakpoint ) ;
inst . hideOnBreakpoint ( ) ;
}
2019-10-28 01:06:02 -04:00
} ) ; * /
2019-03-19 15:03:43 -04:00
}
2020-10-16 15:19:11 -04:00
handleResize ( ) {
if ( this . _resize _timer )
return ;
2020-11-14 21:58:22 -05:00
this . _resize _timer = setTimeout ( ( ) => this . _handleResize ( ) , IS _FIREFOX ? 500 : 100 ) ;
2020-10-16 15:19:11 -04:00
}
_handleResize ( ) {
2020-11-14 14:08:27 -05:00
clearTimeout ( this . _resize _timer ) ;
2020-10-16 15:19:11 -04:00
this . _resize _timer = null ;
2020-11-14 14:08:27 -05:00
if ( ! this . ResizeDetector . instances . size )
this . _needs _resize = true ;
2020-11-14 21:58:22 -05:00
else {
2020-11-14 14:08:27 -05:00
for ( const inst of this . ResizeDetector . instances ) {
2021-02-26 15:35:26 -05:00
inst ? . onScroll ? . ( ) ;
//inst?.props?.onResize?.();
2020-11-14 14:08:27 -05:00
}
2020-11-14 21:58:22 -05:00
this . emit ( 'site.player:fix-player' ) ;
}
2020-10-16 15:19:11 -04:00
}
2019-04-18 03:16:19 -04:00
get is _minimal ( ) {
return this . settings . get ( 'layout.is-minimal' )
}
2020-12-04 14:32:26 -05:00
updateSidebar ( el ) {
if ( ! el )
return ;
if ( ! el . _ffz _update )
el . _ffz _update = debounce ( ( ) => requestAnimationFrame ( ( ) => this . _updateSidebar ( el ) ) , 1000 , 2 ) ;
el . _ffz _update ( ) ;
}
_updateSidebar ( el ) {
if ( ! el )
return ;
const cards = el . querySelectorAll ( '.side-nav-card' ) ,
blocked _games = this . settings . provider . get ( 'directory.game.blocked-games' , [ ] ) ;
for ( const card of cards ) {
const react = this . fine . getReactInstance ( card ) ,
props = react ? . return ? . return ? . memoizedProps ,
stream = props ? . metadataRight ? . props ? . stream ,
rerun = stream ? . type === 'rerun' ? ? false ,
game = stream ? . game ? . displayName ,
offline = props ? . offline ? ? false ;
2022-06-08 23:07:07 -04:00
let should _hide = false ;
if ( game && blocked _games . includes ( game ) )
should _hide = true ;
2022-06-11 12:44:54 -04:00
if ( props ? . isPromoted && this . settings . get ( 'directory.hide-promoted' ) )
2022-06-08 23:07:07 -04:00
should _hide = true ;
else {
2024-10-09 17:09:09 -04:00
if ( ! should _hide ) {
const regexes = this . settings . get ( '__filter:directory.block-users' ) ;
const login = props . userLogin ;
if ( regexes && login ) {
if ( regexes [ 0 ] )
regexes [ 0 ] . lastIndex = - 1 ;
if ( regexes [ 1 ] )
regexes [ 1 ] . lastIndex = - 1 ;
if ( ( regexes [ 0 ] && regexes [ 0 ] . test ( login ) ) || ( regexes [ 1 ] && regexes [ 1 ] . test ( login ) ) )
should _hide = true ;
}
}
if ( ! should _hide ) {
const regexes = this . settings . get ( '__filter:directory.block-titles' ) ;
const title = stream ? . broadcaster ? . broadcastSettings ? . title ;
if ( regexes && title ) {
if ( regexes [ 0 ] )
regexes [ 0 ] . lastIndex = - 1 ;
if ( regexes [ 1 ] )
regexes [ 1 ] . lastIndex = - 1 ;
if ( ( regexes [ 0 ] && regexes [ 0 ] . test ( title ) ) || ( regexes [ 1 ] && regexes [ 1 ] . test ( title ) ) )
should _hide = true ;
}
2024-03-19 16:36:44 -04:00
}
2022-06-08 23:07:07 -04:00
}
2020-12-04 14:32:26 -05:00
card . classList . toggle ( 'ffz--side-nav-card-rerun' , rerun ) ;
card . classList . toggle ( 'ffz--side-nav-card-offline' , offline ) ;
2022-06-08 23:07:07 -04:00
card . classList . toggle ( 'tw-hide' , should _hide ) ;
2020-12-04 14:32:26 -05:00
}
}
/ * u p d a t e C a r d C l a s s ( i n s t ) {
2019-10-23 17:49:01 -05:00
const node = this . fine . getChildNode ( inst ) ;
2020-07-16 16:34:03 -04:00
if ( node ) {
2019-10-23 17:49:01 -05:00
node . classList . toggle ( 'ffz--side-nav-card-rerun' ,
inst . props ? . tooltipContent ? . props ? . streamType === 'rerun'
) ;
2020-07-16 16:34:03 -04:00
node . classList . toggle ( 'ffz--side-nav-card-offline' ,
inst . props ? . offline === true
) ;
const game = inst . props ? . tooltipContent ? . props ? . gameName || inst . props ? . metadataLeft ? . props ? . activity ? . stream ? . game ? . name || inst . props ? . metadataLeft ;
node . classList . toggle ( 'tw-hide' , this . settings . provider . get ( 'directory.game.blocked-games' , [ ] ) . includes ( game ) ) ;
}
2020-12-04 14:32:26 -05:00
} * /
2019-10-23 17:49:01 -05:00
2019-09-28 23:52:50 -04:00
updateNavLinks ( ) {
2019-11-14 19:52:35 -05:00
2019-09-28 23:52:50 -04:00
}
2019-03-19 15:03:43 -04:00
updatePortraitMode ( ) {
2019-12-13 20:14:55 -05:00
2019-03-19 15:03:43 -04:00
}
2024-03-19 16:36:44 -04:00
}