diff --git a/src/main.js b/src/main.js index ddf24ddf..78a5f721 100644 --- a/src/main.js +++ b/src/main.js @@ -100,7 +100,7 @@ class FrankerFaceZ extends Module { FrankerFaceZ.Logger = Logger; const VER = FrankerFaceZ.version_info = { - major: 4, minor: 0, revision: 0, extra: '-rc12.2', + major: 4, minor: 0, revision: 0, extra: '-rc12.3', commit: __git_commit__, build: __webpack_hash__, toString: () => diff --git a/src/modules/metadata.jsx b/src/modules/metadata.jsx index 6a0fbdb9..31270523 100644 --- a/src/modules/metadata.jsx +++ b/src/modules/metadata.jsx @@ -231,6 +231,18 @@ export default class Metadata extends Module { for(const inst of bar.ChannelBar.instances) bar.updateMetadata(inst, keys); } + + const legacy_bar = this.resolve('site.legacy_channel_bar'); + if ( legacy_bar ) { + for(const inst of legacy_bar.ChannelBar.instances) + legacy_bar.updateMetadata(inst, keys); + } + + const game_header = this.resolve('site.directory.game'); + if ( game_header ) { + for(const inst of game_header.GameHeader.instances) + game_header.updateMetadata(inst, keys); + } } @@ -257,7 +269,7 @@ export default class Metadata extends Module { } }; - if ( ! def ) + if ( ! def || !!data.directory !== def.directory ) return destroy(); try { @@ -507,6 +519,24 @@ export default class Metadata extends Module { if ( def.disabled !== undefined ) el.disabled = maybe_call(def.disabled, this, data); + if ( typeof def.button === 'function' ) { + const btn = el.querySelector('button'); + if ( btn ) { + let cls = maybe_call(def.button, this, data); + if ( typeof cls !== 'string' ) + cls = `tw-button--${cls ? 'hollow' : 'text'}`; + + if ( btn._class !== cls ) { + btn._class = cls; + + if ( def.popup && def.click ) + btn.className = `tw-interactive tw-button tw-button--full-width ${cls} ffz-has-stat-arrow`; + else + btn.className = `tw-interactive tw-button tw-button--full-width ${cls}`; + } + } + } + } catch(err) { this.log.capture(err, { tags: { diff --git a/src/sites/twitch-twilight/modules/directory/game.jsx b/src/sites/twitch-twilight/modules/directory/game.jsx index cbcb6254..396e0b97 100644 --- a/src/sites/twitch-twilight/modules/directory/game.jsx +++ b/src/sites/twitch-twilight/modules/directory/game.jsx @@ -16,27 +16,153 @@ export default class Game extends SiteModule { this.inject('site.fine'); this.inject('site.apollo'); + this.inject('metadata'); this.inject('i18n'); this.inject('settings'); - this.GameHeader = this.fine.define( - 'game-header', + this.metadata.definitions.block_game = { + directory: true, + button(data) { + return `ffz-directory-toggle-block${data.blocked ? ' active' : ''}` + }, + + setup(data) { + if ( data.type !== 'GAMES' ) + return null; + + const blocked_games = this.settings.provider.get('directory.game.blocked-games', []), + blocked = blocked_games.includes(data.name); + + data.blocked = blocked; + return data; + }, + + label(data) { + if ( ! data ) + return null; + + return data.blocked ? + this.i18n.t('directory.unblock', 'Unblock') : + this.i18n.t('directory.block', 'Block') + }, + + tooltip() { + return this.i18n.t('directory.block-explain', 'This will let you block streams playing this game from showing up in the directory.'); + }, + + click: this.generateClickHandler('directory.game.blocked-games') + } + + this.metadata.definitions.hide_thumbnails = { + directory: true, + button(data) { + return `ffz-directory-toggle-thumbnail${data.hidden ? ' active' : ''}` + }, + + setup(data) { + if ( data.type !== 'GAMES' ) + return null; + + const hidden_games = this.settings.provider.get('directory.game.hidden-thumbnails', []); + + data.hidden = hidden_games.includes(data.name); + return data; + }, + + label(data) { + if ( ! data ) + return null; + + return data.hidden ? + this.i18n.t('directory.show-thumbnails', 'Show Thumbnails') : + this.i18n.t('directory.hide-thumbnails', 'Hide Thumbnails'); + }, + + tooltip() { + return this.i18n.t('directory.thumbnails-explain', 'Enabling this will hide thumbnails of this game everywhere in the directory.'); + }, + + click: this.generateClickHandler('directory.game.hidden-thumbnails') + } + + this.LegacyGameHeader = this.fine.define( + 'legacy-game-header', n => n.renderFollowButton && n.renderGameDetailsTab, ['dir-game-index', 'dir-community'] ); + this.GameHeader = this.fine.define( + 'game-header', + n => n.renderDirectoryMetadata, + ['dir-game-index', 'dir-community', 'dir-game-videos', 'dir-game-clips', 'dir-game-details'] + ); + this.apollo.registerModifier('GamePage_Game_RENAME2', GAME_QUERY); } onEnable() { + this.GameHeader.on('unmount', this.unmountGameHeader, this); + this.GameHeader.on('mount', this.updateGameHeader, this); + this.GameHeader.on('update', this.updateGameHeader, this); + this.GameHeader.ready((cls, instances) => { + this.settings.updateContext({new_channel: true}); + + for(const inst of instances) + this.updateGameHeader(inst); + }); + + this.LegacyGameHeader.ready((cls, instances) => { for(const inst of instances) this.updateButtons(inst); }); - this.GameHeader.on('update', this.updateButtons, this); + this.LegacyGameHeader.on('update', this.updateButtons, this); } + + unmountGameHeader(inst) { // eslint-disable-line class-methods-use-this + const timers = inst._ffz_meta_timers; + if ( timers ) + for(const key in timers) + if ( timers[key] ) + clearTimeout(timers[key]); + } + + + updateGameHeader(inst) { + this.updateMetadata(inst); + } + + + updateMetadata(inst, keys) { + const container = this.fine.getChildNode(inst), + wrapper = container && container.querySelector && container.querySelector('.side-nav-directory-info__info-wrapper > div + div'); + + if ( ! inst._ffz_mounted || ! wrapper ) + return; + + const metabar = wrapper; + + if ( ! keys ) + keys = this.metadata.keys; + else if ( ! Array.isArray(keys) ) + keys = [keys]; + + const timers = inst._ffz_meta_timers = inst._ffz_meta_timers || {}, + refresh_func = key => this.updateMetadata(inst, key), + data = { + directory: inst.props.data.directory, + type: inst.props.directoryType, + name: inst.props.directoryName, + _inst: inst + } + + for(const key of keys) + this.metadata.render(key, data, metabar, timers, refresh_func); + } + + updateButtons(inst, update = false) { const container = this.fine.getChildNode(inst); if ( inst.props.directoryType !== 'GAMES' || ! container || ! container.querySelector ) @@ -69,7 +195,7 @@ export default class Game extends SiteModule { block_btn = (); @@ -91,7 +217,7 @@ export default class Game extends SiteModule { hidden_btn = () @@ -104,7 +230,7 @@ export default class Game extends SiteModule { ); } - generateClickHandler(setting, game, update_func) { + generateLegacyClickHandler(setting, game, update_func) { return e => { e.preventDefault(); const values = this.settings.provider.get(setting) || [], @@ -120,4 +246,20 @@ export default class Game extends SiteModule { } } + generateClickHandler(setting) { + return (data, event, update_func) => { + const values = this.settings.provider.get(setting, []), + game = data.name, + idx = values.indexOf(game); + + if ( idx === -1 ) + values.push(game) + else + values.splice(idx, 1); + + this.settings.provider.set(setting, values); + this.parent.DirectoryCard.forceUpdate(); + update_func(); + } + } } \ No newline at end of file diff --git a/src/sites/twitch-twilight/modules/sub_button.jsx b/src/sites/twitch-twilight/modules/sub_button.jsx index 1d6ae33b..2c31de46 100644 --- a/src/sites/twitch-twilight/modules/sub_button.jsx +++ b/src/sites/twitch-twilight/modules/sub_button.jsx @@ -38,9 +38,33 @@ export default class SubButton extends Module { } onEnable() { + this.settings.on(':changed:layout.swap-sidebars', () => this.SubButton.forceUpdate()) + this.SubButton.ready((cls, instances) => { + const t = this, + old_render = cls.prototype.render; + + cls.prototype.render = function() { + try { + const old_direction = this.props.balloonDirection; + if ( old_direction !== undefined ) { + const should_be_left = t.settings.get('layout.swap-sidebars'), + is_left = old_direction.includes('--left'); + + if ( should_be_left && ! is_left ) + this.props.balloonDirection = old_direction.replace('--right', '--left'); + else if ( ! should_be_left && is_left ) + this.props.balloonDirection = old_direction.replace('--left', '--right'); + } + } catch(err) { /* no-op */ } + + return old_render.call(this); + } + for(const inst of instances) this.updateSubButton(inst); + + this.SubButton.forceUpdate(); }); this.SubButton.on('mount', this.updateSubButton, this); @@ -50,7 +74,7 @@ export default class SubButton extends Module { updateSubButton(inst) { const container = this.fine.getChildNode(inst), - btn = container && container.querySelector('button.tw-button--dropmenu'); + btn = container && container.querySelector('button[data-a-target="subscribe-button"]'); if ( ! btn ) return; @@ -69,7 +93,13 @@ export default class SubButton extends Module { /> , btn.firstElementChild); - } else if ( ! should_show && icon ) + btn.appendChild(); + + } else if ( ! should_show && icon ) { icon.remove(); + const post = btn.querySelector('.ffz--post-prime'); + if ( post ) + post.remove(); + } } } \ No newline at end of file diff --git a/src/sites/twitch-twilight/styles/channel.scss b/src/sites/twitch-twilight/styles/channel.scss index 969dfffd..0c05af41 100644 --- a/src/sites/twitch-twilight/styles/channel.scss +++ b/src/sites/twitch-twilight/styles/channel.scss @@ -79,4 +79,13 @@ @keyframes ffz-fade-in { from {opacity: 0} to {opacity: 1} +} + + +.side-nav { + .ffz--can-prime, + .ffz--post-prime { + width: 0; + flex-grow: 1; + } } \ No newline at end of file