mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-03 17:48:30 +00:00
4.37.0
* Added: "Copy Message" chat action for copying a message to your clipboard. * Added: Setting to pause the player by clicking on it. This is disabled by default, and the pause happens after half a second to avoid pausing as part of a double-click. * Added: Setting to clear the emote menu's search when closing it. * Added: Setting to hide the "Elevate Your Message" button in the chat input field. * Changed: Remove code related to channel hosting. * Fixed: Do not attempt to load FFZ on `gql` or `passport` subdomains. * Fixed: Channel leader-boards not being hidden on channels within a specific experiment.
This commit is contained in:
parent
bc0eab4409
commit
8cd6545556
16 changed files with 185 additions and 660 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.36.2",
|
"version": "4.37.0",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
@ -12,9 +12,9 @@
|
||||||
"dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --config webpack.web.dev.js",
|
"dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --config webpack.web.dev.js",
|
||||||
"dev:prod": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --config webpack.web.dev.prod.js",
|
"dev:prod": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --config webpack.web.dev.prod.js",
|
||||||
"build": "pnpm build:prod",
|
"build": "pnpm build:prod",
|
||||||
"build:stats": "cross-env NODE_ENV=production webpack --config webpack.web.prod.js --json > stats.json",
|
"build:stats": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack --config webpack.web.prod.js --json > stats.json",
|
||||||
"build:prod": "cross-env NODE_ENV=production webpack --config webpack.web.prod.js",
|
"build:prod": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack --config webpack.web.prod.js",
|
||||||
"build:dev": "pnpm clean && webpack --config webpack.web.dev.js",
|
"build:dev": "pnpm clean && cross-env NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack.web.dev.js",
|
||||||
"font": "pnpm font:edit",
|
"font": "pnpm font:edit",
|
||||||
"font:edit": "fontello-cli --cli-config fontello.client.json edit",
|
"font:edit": "fontello-cli --cli-config fontello.client.json edit",
|
||||||
"font:save": "fontello-cli --cli-config fontello.client.json save && pnpm font:update",
|
"font:save": "fontello-cli --cli-config fontello.client.json save && pnpm font:update",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
(() => {
|
(() => {
|
||||||
// Don't run on certain sub-domains.
|
// Don't run on certain sub-domains.
|
||||||
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev)\./.test(location.hostname) )
|
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const DEBUG = localStorage.ffzDebugMode == 'true' && document.body.classList.contains('ffz-dev'),
|
const DEBUG = localStorage.ffzDebugMode == 'true' && document.body.classList.contains('ffz-dev'),
|
||||||
|
|
29
src/modules/chat/actions/components/edit-copy.vue
Normal file
29
src/modules/chat/actions/components/edit-copy.vue
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="tw-flex tw-align-items-start">
|
||||||
|
<label for="edit_format" class="tw-mg-y-05">
|
||||||
|
{{ t('setting.actions.format', 'Format') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="tw-full-width">
|
||||||
|
<input
|
||||||
|
id="edit_format"
|
||||||
|
v-model="value.format"
|
||||||
|
:placeholder="defaults.format"
|
||||||
|
class="tw-border-radius-medium tw-font-size-6 tw-full-width ffz-input tw-pd-x-1 tw-pd-y-05 tw-mg-y-05"
|
||||||
|
@input="$emit('input', value)"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="tw-c-text-alt-2 tw-mg-b-1">
|
||||||
|
{{ t('setting.actions.variables', 'Available Variables: {vars}', {vars}) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['value', 'defaults', 'vars'],
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -83,6 +83,51 @@ export const edit_overrides = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Copy to Clipboard
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export const copy_message = {
|
||||||
|
presets: [{
|
||||||
|
appearance: {
|
||||||
|
type: 'icon',
|
||||||
|
icon: 'ffz-i-docs'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
|
defaults: {
|
||||||
|
format: '{{user.displayName}}: {{message.text}}'
|
||||||
|
},
|
||||||
|
|
||||||
|
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-copy.vue'),
|
||||||
|
|
||||||
|
required_context: ['user', 'message'],
|
||||||
|
|
||||||
|
title: 'Copy Message',
|
||||||
|
description: 'Allows you to quickly copy a chat message to your clipboard.',
|
||||||
|
|
||||||
|
can_self: true,
|
||||||
|
|
||||||
|
tooltip(data) {
|
||||||
|
const msg = this.replaceVariables(data.options.format, data);
|
||||||
|
|
||||||
|
return [
|
||||||
|
(<div class="tw-border-b tw-mg-b-05">{ // eslint-disable-line react/jsx-key
|
||||||
|
this.i18n.t('chat.actions.copy_message', 'Copy Message')
|
||||||
|
}</div>),
|
||||||
|
(<div class="tw-align-left">{ // eslint-disable-line react/jsx-key
|
||||||
|
msg
|
||||||
|
}</div>)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
click(event, data) {
|
||||||
|
const msg = this.replaceVariables(data.options.format, data);
|
||||||
|
navigator.clipboard.writeText(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Open URL
|
// Open URL
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
@ -559,6 +559,15 @@ export default class PlayerBase extends Module {
|
||||||
},
|
},
|
||||||
changed: val => this.css_tweaks.toggle('player-hide-mouse', val)
|
changed: val => this.css_tweaks.toggle('player-hide-mouse', val)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('player.single-click-pause', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Player > General >> Playback',
|
||||||
|
title: "Pause/Unpause the player by clicking.",
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
|
@ -643,8 +652,6 @@ export default class PlayerBase extends Module {
|
||||||
|
|
||||||
|
|
||||||
onShortcut(e) {
|
onShortcut(e) {
|
||||||
this.log.info('Compressor Hotkey', e);
|
|
||||||
|
|
||||||
for(const inst of this.Player.instances)
|
for(const inst of this.Player.instances)
|
||||||
this.compressPlayer(inst, e);
|
this.compressPlayer(inst, e);
|
||||||
}
|
}
|
||||||
|
@ -825,10 +832,14 @@ export default class PlayerBase extends Module {
|
||||||
if ( ! this._ffz_click_handler )
|
if ( ! this._ffz_click_handler )
|
||||||
this._ffz_click_handler = this.ffzClickHandler.bind(this);
|
this._ffz_click_handler = this.ffzClickHandler.bind(this);
|
||||||
|
|
||||||
|
if ( ! this._ffz_dblclick_handler )
|
||||||
|
this._ffz_dblclick_handler = this.ffzDblClickHandler.bind(this);
|
||||||
|
|
||||||
if ( ! this._ffz_menu_handler )
|
if ( ! this._ffz_menu_handler )
|
||||||
this._ffz_menu_handler = this.ffzMenuHandler.bind(this);
|
this._ffz_menu_handler = this.ffzMenuHandler.bind(this);
|
||||||
|
|
||||||
on(cont, 'wheel', this._ffz_scroll_handler);
|
on(cont, 'wheel', this._ffz_scroll_handler);
|
||||||
|
on(cont, 'dblclick', this._ffz_dblclick_handler);
|
||||||
on(cont, 'mousedown', this._ffz_click_handler);
|
on(cont, 'mousedown', this._ffz_click_handler);
|
||||||
on(cont, 'contextmenu', this._ffz_menu_handler);
|
on(cont, 'contextmenu', this._ffz_menu_handler);
|
||||||
}
|
}
|
||||||
|
@ -853,23 +864,60 @@ export default class PlayerBase extends Module {
|
||||||
this._ffz_menu_handler = null;
|
this._ffz_menu_handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this._ffz_dblclick_handler ) {
|
||||||
|
off(cont, 'dblclick', this._ffz_dblclick_handler);
|
||||||
|
this._ffz_dblclick_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
this._ffz_listeners = false;
|
this._ffz_listeners = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzDelayPause = function() {
|
||||||
|
if ( this._ffz_pause_timer )
|
||||||
|
clearTimeout(this._ffz_pause_timer);
|
||||||
|
|
||||||
|
const player = this.props?.mediaPlayerInstance;
|
||||||
|
if (! player.isPaused())
|
||||||
|
this._ffz_pause_timer = setTimeout(() => {
|
||||||
|
const player = this.props?.mediaPlayerInstance;
|
||||||
|
if (!player.isPaused())
|
||||||
|
player.pause();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzDblClickHandler = function(event) {
|
||||||
|
if ( ! event )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( this._ffz_pause_timer )
|
||||||
|
clearTimeout(this._ffz_pause_timer);
|
||||||
|
}
|
||||||
|
|
||||||
cls.prototype.ffzClickHandler = function(event) {
|
cls.prototype.ffzClickHandler = function(event) {
|
||||||
if ( ! event )
|
if ( ! event )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const vol_scroll = t.settings.get('player.volume-scroll'),
|
const vol_scroll = t.settings.get('player.volume-scroll'),
|
||||||
gain_scroll = t.settings.get('player.gain.scroll'),
|
gain_scroll = t.settings.get('player.gain.scroll'),
|
||||||
|
click_pause = t.settings.get('player.single-click-pause'),
|
||||||
|
|
||||||
wants_rmb = wantsRMB(vol_scroll) || wantsRMB(gain_scroll);
|
wants_rmb = wantsRMB(vol_scroll) || wantsRMB(gain_scroll);
|
||||||
|
|
||||||
|
// Left Click
|
||||||
|
if (click_pause && event.button === 0) {
|
||||||
|
if (! event.target || ! event.target.classList.contains('click-handler'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ffzDelayPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right Click
|
||||||
if ( wants_rmb && event.button === 2 ) {
|
if ( wants_rmb && event.button === 2 ) {
|
||||||
this.ffz_rmb = true;
|
this.ffz_rmb = true;
|
||||||
this.ffz_scrolled = false;
|
this.ffz_scrolled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middle Click
|
||||||
if ( ! t.settings.get('player.mute-click') || event.button !== 1 )
|
if ( ! t.settings.get('player.mute-click') || event.button !== 1 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ export default class Channel extends Module {
|
||||||
changed: () => this.updateLinks()
|
changed: () => this.updateLinks()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('channel.hosting.enable', {
|
/*this.settings.add('channel.hosting.enable', {
|
||||||
default: true,
|
default: true,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Channel > Behavior >> Hosting',
|
path: 'Channel > Behavior >> Hosting',
|
||||||
|
@ -87,8 +87,7 @@ export default class Channel extends Module {
|
||||||
component: 'setting-check-box'
|
component: 'setting-check-box'
|
||||||
},
|
},
|
||||||
changed: val => ! val && this.InfoBar.each(el => this.updateBar(el))
|
changed: val => ! val && this.InfoBar.each(el => this.updateBar(el))
|
||||||
});
|
});*/
|
||||||
|
|
||||||
|
|
||||||
this.ChannelPanels = this.fine.define(
|
this.ChannelPanels = this.fine.define(
|
||||||
'channel-panels',
|
'channel-panels',
|
||||||
|
@ -116,7 +115,7 @@ export default class Channel extends Module {
|
||||||
{childNodes: true, subtree: true}, 1
|
{childNodes: true, subtree: true}, 1
|
||||||
);
|
);
|
||||||
|
|
||||||
const strip_host = resp => {
|
/*const strip_host = resp => {
|
||||||
if ( this.settings.get('channel.hosting.enable') )
|
if ( this.settings.get('channel.hosting.enable') )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ export default class Channel extends Module {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.apollo.registerModifier('UseHosting', strip_host, false);
|
this.apollo.registerModifier('UseHosting', strip_host, false);
|
||||||
this.apollo.registerModifier('PlayerTrackingContextQuery', strip_host, false);
|
this.apollo.registerModifier('PlayerTrackingContextQuery', strip_host, false);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -162,7 +161,7 @@ export default class Channel extends Module {
|
||||||
this.InfoBar.on('unmount', this.removeBar, this);
|
this.InfoBar.on('unmount', this.removeBar, this);
|
||||||
this.InfoBar.each(el => this.updateBar(el));
|
this.InfoBar.each(el => this.updateBar(el));
|
||||||
|
|
||||||
this.subpump.on(':pubsub-message', this.onPubSub, this);
|
//this.subpump.on(':pubsub-message', this.onPubSub, this);
|
||||||
|
|
||||||
this.router.on(':route', this.checkNavigation, this);
|
this.router.on(':route', this.checkNavigation, this);
|
||||||
this.checkNavigation();
|
this.checkNavigation();
|
||||||
|
@ -230,7 +229,7 @@ export default class Channel extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHost(channel_id, channel_login, target_id, target_login) {
|
/*setHost(channel_id, channel_login, target_id, target_login) {
|
||||||
const topic = `stream-chat-room-v1.${channel_id}`;
|
const topic = `stream-chat-room-v1.${channel_id}`;
|
||||||
|
|
||||||
this.subpump.inject(topic, {
|
this.subpump.inject(topic, {
|
||||||
|
@ -272,7 +271,7 @@ export default class Channel extends Module {
|
||||||
event.message.data.num_viewers = 0;
|
event.message.data.num_viewers = 0;
|
||||||
event.markChanged();
|
event.markChanged();
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
updateSubscription(login) {
|
updateSubscription(login) {
|
||||||
|
@ -441,8 +440,8 @@ export default class Channel extends Module {
|
||||||
});
|
});
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if ( ! this.settings.get('channel.hosting.enable') && props.hostLogin )
|
//if ( ! this.settings.get('channel.hosting.enable') && props.hostLogin )
|
||||||
this.setHost(props.channelID, props.channelLogin, null, null);
|
// this.setHost(props.channelID, props.channelLogin, null, null);
|
||||||
|
|
||||||
this.updateSubscription(props.channelLogin);
|
this.updateSubscription(props.channelLogin);
|
||||||
this.updateMetadata(el);
|
this.updateMetadata(el);
|
||||||
|
@ -492,10 +491,10 @@ export default class Channel extends Module {
|
||||||
live_since: props.liveSince
|
live_since: props.liveSince
|
||||||
},
|
},
|
||||||
props,
|
props,
|
||||||
hosted: {
|
/*hosted: {
|
||||||
login: props.hostLogin,
|
login: props.hostLogin,
|
||||||
display_name: props.hostDisplayName
|
display_name: props.hostDisplayName
|
||||||
},
|
},*/
|
||||||
el,
|
el,
|
||||||
getViewerCount: () => {
|
getViewerCount: () => {
|
||||||
const thing = cont.querySelector('p[data-a-target="animated-channel-viewers-count"]'),
|
const thing = cont.querySelector('p[data-a-target="animated-channel-viewers-count"]'),
|
||||||
|
|
|
@ -188,6 +188,15 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.emote-menu.clear-search', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Emote Menu >> General',
|
||||||
|
title: 'Reset search when closing the Emote Menu.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.emote-menu.enabled', {
|
this.settings.add('chat.emote-menu.enabled', {
|
||||||
default: true,
|
default: true,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -1078,6 +1087,7 @@ export default class EmoteMenu extends Module {
|
||||||
reducedPadding: t.chat.context.get('chat.emote-menu.reduced-padding'),
|
reducedPadding: t.chat.context.get('chat.emote-menu.reduced-padding'),
|
||||||
combineTabs: t.chat.context.get('chat.emote-menu.combine-tabs'),
|
combineTabs: t.chat.context.get('chat.emote-menu.combine-tabs'),
|
||||||
showSearch: t.chat.context.get('chat.emote-menu.show-search'),
|
showSearch: t.chat.context.get('chat.emote-menu.show-search'),
|
||||||
|
clearSearch: t.chat.context.get('chat.emote-menu.clear-search'),
|
||||||
tone: t.settings.provider.get('emoji-tone', null)
|
tone: t.settings.provider.get('emoji-tone', null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1226,6 +1236,7 @@ export default class EmoteMenu extends Module {
|
||||||
t.chat.context.on('changed:chat.emote-menu.show-heading', this.updateSettingState, this);
|
t.chat.context.on('changed:chat.emote-menu.show-heading', this.updateSettingState, this);
|
||||||
t.chat.context.on('changed:chat.emote-menu.combine-tabs', this.updateSettingState, this);
|
t.chat.context.on('changed:chat.emote-menu.combine-tabs', this.updateSettingState, this);
|
||||||
t.chat.context.on('changed:chat.emote-menu.show-search', this.updateSettingState, this);
|
t.chat.context.on('changed:chat.emote-menu.show-search', this.updateSettingState, this);
|
||||||
|
t.chat.context.on('changed:chat.emote-menu.clear-search', this.updateSettingState, this);
|
||||||
t.chat.context.on('changed:chat.emote-menu.tall', this.updateSettingState, this);
|
t.chat.context.on('changed:chat.emote-menu.tall', this.updateSettingState, this);
|
||||||
|
|
||||||
window.ffz_menu = this;
|
window.ffz_menu = this;
|
||||||
|
@ -1241,6 +1252,7 @@ export default class EmoteMenu extends Module {
|
||||||
t.chat.context.off('changed:chat.emote-menu.reduced-padding', this.updateSettingState, this);
|
t.chat.context.off('changed:chat.emote-menu.reduced-padding', this.updateSettingState, this);
|
||||||
t.chat.context.off('changed:chat.emote-menu.combine-tabs', this.updateSettingState, this);
|
t.chat.context.off('changed:chat.emote-menu.combine-tabs', this.updateSettingState, this);
|
||||||
t.chat.context.off('changed:chat.emote-menu.show-search', this.updateSettingState, this);
|
t.chat.context.off('changed:chat.emote-menu.show-search', this.updateSettingState, this);
|
||||||
|
t.chat.context.off('changed:chat.emote-menu.clear-search', this.updateSettingState, this);
|
||||||
t.chat.context.off('changed:chat.emote-menu.tall', this.updateSettingState, this);
|
t.chat.context.off('changed:chat.emote-menu.tall', this.updateSettingState, this);
|
||||||
|
|
||||||
if ( window.ffz_menu === this )
|
if ( window.ffz_menu === this )
|
||||||
|
@ -1256,6 +1268,7 @@ export default class EmoteMenu extends Module {
|
||||||
reducedPadding: t.chat.context.get('chat.emote-menu.reduced-padding'),
|
reducedPadding: t.chat.context.get('chat.emote-menu.reduced-padding'),
|
||||||
combineTabs: t.chat.context.get('chat.emote-menu.combine-tabs'),
|
combineTabs: t.chat.context.get('chat.emote-menu.combine-tabs'),
|
||||||
showSearch: t.chat.context.get('chat.emote-menu.show-search'),
|
showSearch: t.chat.context.get('chat.emote-menu.show-search'),
|
||||||
|
clearSearch: t.chat.context.get('chat.emote-menu.clear-search'),
|
||||||
tall: t.chat.context.get('chat.emote-menu.tall')
|
tall: t.chat.context.get('chat.emote-menu.tall')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2309,6 +2322,13 @@ export default class EmoteMenu extends Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! this.props.visible && old_props.visible ) {
|
||||||
|
if ( this.state.clearSearch ) {
|
||||||
|
this.setState(this.filterState('', this.state));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cd = this.props.channel_data,
|
const cd = this.props.channel_data,
|
||||||
old_cd = old_props.channel_data,
|
old_cd = old_props.channel_data,
|
||||||
cd_diff = cd?.user !== old_cd?.user || cd?.channel !== old_cd?.channel,
|
cd_diff = cd?.user !== old_cd?.user || cd?.channel !== old_cd?.channel,
|
||||||
|
|
|
@ -201,7 +201,7 @@ export default class ChatHook extends Module {
|
||||||
|
|
||||||
this.ChatController = this.fine.define(
|
this.ChatController = this.fine.define(
|
||||||
'chat-controller',
|
'chat-controller',
|
||||||
n => n.hostingHandler && n.onRoomStateUpdated,
|
n => n.parseOutgoingMessage && n.onRoomStateUpdated && n.renderNotifications,
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -656,6 +656,15 @@ export default class ChatHook extends Module {
|
||||||
component: 'setting-check-box'
|
component: 'setting-check-box'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.input.show-elevate-your-message', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Input >> Appearance',
|
||||||
|
title: 'Allow the "Elevate Your Message" button to be displayed.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentChat() {
|
get currentChat() {
|
||||||
|
@ -941,6 +950,9 @@ export default class ChatHook extends Module {
|
||||||
this.updateMentionCSS();
|
this.updateMentionCSS();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.chat.context.getChanges('chat.input.show-elevate-your-message', val =>
|
||||||
|
this.css_tweaks.toggleHide('elevate-your-message', ! val));
|
||||||
|
|
||||||
this.updateChatCSS();
|
this.updateChatCSS();
|
||||||
this.updateColors();
|
this.updateColors();
|
||||||
this.updateLineBorders();
|
this.updateLineBorders();
|
||||||
|
@ -2441,7 +2453,7 @@ export default class ChatHook extends Module {
|
||||||
return old_points.call(i, e);
|
return old_points.call(i, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const old_host = this.onHostingEvent;
|
/*const old_host = this.onHostingEvent;
|
||||||
this.onHostingEvent = function (e, _t) {
|
this.onHostingEvent = function (e, _t) {
|
||||||
t.emit('tmi:host', e, _t);
|
t.emit('tmi:host', e, _t);
|
||||||
return old_host.call(i, e, _t);
|
return old_host.call(i, e, _t);
|
||||||
|
@ -2451,7 +2463,7 @@ export default class ChatHook extends Module {
|
||||||
this.onUnhostEvent = function (e, _t) {
|
this.onUnhostEvent = function (e, _t) {
|
||||||
t.emit('tmi:unhost', e, _t);
|
t.emit('tmi:unhost', e, _t);
|
||||||
return old_unhost.call(i, e, _t);
|
return old_unhost.call(i, e, _t);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
const old_add = this.addMessage;
|
const old_add = this.addMessage;
|
||||||
this.addMessage = function(e) {
|
this.addMessage = function(e) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ const CLASSES = {
|
||||||
'modview-hide-info': '.modview-player-widget__hide-stream-info',
|
'modview-hide-info': '.modview-player-widget__hide-stream-info',
|
||||||
|
|
||||||
'community-highlights': '.community-highlight-stack__card',
|
'community-highlights': '.community-highlight-stack__card',
|
||||||
|
'elevate-your-message': '.chat-input__input-icons button[aria-label="ElevatedMessage"]',
|
||||||
|
|
||||||
'prime-offers': '.top-nav__prime',
|
'prime-offers': '.top-nav__prime',
|
||||||
'discover-luna': '.top-nav__external-link[data-a-target="try-presto-link"]',
|
'discover-luna': '.top-nav__external-link[data-a-target="try-presto-link"]',
|
||||||
|
@ -38,7 +39,7 @@ const CLASSES = {
|
||||||
'player-event-bar': '.channel-root .live-event-banner-ui__header',
|
'player-event-bar': '.channel-root .live-event-banner-ui__header',
|
||||||
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
||||||
|
|
||||||
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard',
|
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard,.channel-leaderboard-marquee',
|
||||||
'whispers': 'body .whispers-open-threads,.tw-core-button[data-a-target="whisper-box-button"],.whispers__pill',
|
'whispers': 'body .whispers-open-threads,.tw-core-button[data-a-target="whisper-box-button"],.whispers__pill',
|
||||||
|
|
||||||
'dir-live-ind': '.live-channel-card[data-ffz-type="live"] .tw-channel-status-text-indicator, article[data-ffz-type="live"] .tw-channel-status-text-indicator',
|
'dir-live-ind': '.live-channel-card[data-ffz-type="live"] .tw-channel-status-text-indicator, article[data-ffz-type="live"] .tw-channel-status-text-indicator',
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default class Dashboard extends Module {
|
||||||
this.settings.updateContext({
|
this.settings.updateContext({
|
||||||
channel: get('props.channelLogin', inst),
|
channel: get('props.channelLogin', inst),
|
||||||
channelID: get('props.channelID', inst),
|
channelID: get('props.channelID', inst),
|
||||||
hosting: !! inst.props?.hostedChannel?.id
|
//hosting: !! inst.props?.hostedChannel?.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ export default class Dashboard extends Module {
|
||||||
this.settings.updateContext({
|
this.settings.updateContext({
|
||||||
channel: null,
|
channel: null,
|
||||||
channelID: null,
|
channelID: null,
|
||||||
hosting: false
|
//hosting: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,249 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Following Page
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
import {SiteModule} from 'utilities/module';
|
|
||||||
import {createElement} from 'utilities/dom';
|
|
||||||
import {get} from 'utilities/object';
|
|
||||||
|
|
||||||
import {createPopper} from '@popperjs/core';
|
|
||||||
import {makeReference} from 'utilities/tooltip';
|
|
||||||
|
|
||||||
export default class Following extends SiteModule {
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
|
|
||||||
this.inject('site.fine');
|
|
||||||
this.inject('site.router');
|
|
||||||
this.inject('site.apollo');
|
|
||||||
this.inject('site.css_tweaks');
|
|
||||||
|
|
||||||
this.inject('i18n');
|
|
||||||
this.inject('settings');
|
|
||||||
|
|
||||||
this.settings.add('directory.following.group-hosts', {
|
|
||||||
default: true,
|
|
||||||
|
|
||||||
ui: {
|
|
||||||
path: 'Directory > Following @{"description": "**Note:** These settings do not currently work due to changes made by Twitch to how the directory works."} >> Hosts',
|
|
||||||
title: 'Group Hosts',
|
|
||||||
description: 'Only show a given hosted channel once in the directory.',
|
|
||||||
component: 'setting-check-box'
|
|
||||||
},
|
|
||||||
|
|
||||||
changed: () => {
|
|
||||||
this.apollo.maybeRefetch('FollowedIndex_CurrentUser');
|
|
||||||
this.apollo.maybeRefetch('FollowingHosts_CurrentUser');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.settings.add('directory.following.host-menus', {
|
|
||||||
default: 1,
|
|
||||||
|
|
||||||
ui: {
|
|
||||||
path: 'Directory > Following >> Hosts',
|
|
||||||
title: 'Hosted Channel Menus',
|
|
||||||
description: 'Display a menu to select which channel to visit when clicking a hosted channel in the directory.',
|
|
||||||
|
|
||||||
component: 'setting-select-box',
|
|
||||||
|
|
||||||
data: [
|
|
||||||
{value: 0, title: 'Disabled'},
|
|
||||||
{value: 1, title: 'When Multiple are Hosting'},
|
|
||||||
{value: 2, title: 'Always'}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
changed: () => this.parent.DirectoryCard.forceUpdate()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hosts = new WeakMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifyLiveUsers(res, path = 'followedLiveUsers') {
|
|
||||||
const followed_live = get(`data.currentUser.${path}`, res);
|
|
||||||
if ( ! followed_live )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( followed_live.nodes )
|
|
||||||
followed_live.nodes = this.parent.processNodes(followed_live.nodes);
|
|
||||||
|
|
||||||
else if ( followed_live.edges )
|
|
||||||
followed_live.edges = this.parent.processNodes(followed_live.edges);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifyLiveHosts(res) {
|
|
||||||
const blocked_games = this.settings.provider.get('directory.game.blocked-games', []),
|
|
||||||
do_grouping = this.settings.get('directory.following.group-hosts'),
|
|
||||||
edges = get('data.currentUser.followedHosts.nodes', res);
|
|
||||||
|
|
||||||
if ( ! edges || ! edges.length )
|
|
||||||
return res;
|
|
||||||
|
|
||||||
this.hosts = new WeakMap();
|
|
||||||
const out = [];
|
|
||||||
|
|
||||||
for(const edge of edges) {
|
|
||||||
if ( ! edge )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const node = edge.node || edge,
|
|
||||||
hosted = node.hosting,
|
|
||||||
stream = hosted && hosted.stream;
|
|
||||||
|
|
||||||
if ( ! stream || stream.game && blocked_games.includes(stream.game.name) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ( ! stream.viewersCount ) {
|
|
||||||
if ( ! do_grouping || ! this.hosts[hosted.login] )
|
|
||||||
out.push(edge);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = stream.viewersCount = new Number(stream.viewersCount || 0);
|
|
||||||
|
|
||||||
store.createdAt = stream.createdAt;
|
|
||||||
store.title = stream.title;
|
|
||||||
//store.game = stream.game;
|
|
||||||
|
|
||||||
if ( do_grouping ) {
|
|
||||||
const host_nodes = this.hosts[hosted.login];
|
|
||||||
if ( host_nodes ) {
|
|
||||||
host_nodes.push(node);
|
|
||||||
this.hosts.set(store, host_nodes);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.hosts.set(store, this.hosts[hosted.login] = [node]);
|
|
||||||
out.push(edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
out.push(edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.data.currentUser.followedHosts.nodes = out;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnable() {
|
|
||||||
document.body.addEventListener('click', this.destroyHostMenu.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyHostMenu(event) {
|
|
||||||
if (!event || ! this.hostMenu || event && event.target && event.target.closest('.ffz-channel-selector-outer') === null && Date.now() > this.hostMenuBuffer) {
|
|
||||||
this.hostMenuPopper && this.hostMenuPopper.destroy();
|
|
||||||
this.hostMenu && this.hostMenu.remove();
|
|
||||||
this.hostMenuPopper = this.hostMenu = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showHostMenu(inst, channels, event) {
|
|
||||||
if (this.settings.get('directory.following.host-menus') === 0 || this.settings.get('directory.following.host-menus') === 1 && channels.length < 2) return;
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
this.hostMenuPopper && this.hostMenuPopper.destroy();
|
|
||||||
|
|
||||||
this.hostMenu && this.hostMenu.remove();
|
|
||||||
|
|
||||||
const simplebarContentChildren = [];
|
|
||||||
|
|
||||||
// Hosted Channel Header
|
|
||||||
simplebarContentChildren.push(<p class="tw-pd-t-05 tw-pd-x-1 tw-c-text-alt-2">
|
|
||||||
{this.i18n.t('directory.hosted', 'Hosted Channel')}
|
|
||||||
</p>);
|
|
||||||
|
|
||||||
// Hosted Channel Content
|
|
||||||
simplebarContentChildren.push(<a
|
|
||||||
class="tw-block tw-border-radius-small tw-full-width ffz-interactable ffz-interactable--default ffz-interactable--hover-enabled tw-interactive"
|
|
||||||
href={`/${inst.props.channelLogin}`}
|
|
||||||
onClick={e => this.parent.hijackUserClick(e, inst.props.channelLogin, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
|
|
||||||
>
|
|
||||||
<div class="tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1 tw-mg-y-05">
|
|
||||||
<div class="ffz-channel-avatar">
|
|
||||||
<img src={inst.props.channelImageProps.src} alt={inst.props.channelDisplayName} />
|
|
||||||
</div>
|
|
||||||
<p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">
|
|
||||||
{inst.props.channelDisplayName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</a>);
|
|
||||||
|
|
||||||
// Hosting Channels Header
|
|
||||||
simplebarContentChildren.push(<p class="tw-pd-t-05 tw-pd-x-1 tw-c-text-alt-2">
|
|
||||||
{this.i18n.t('directory.hosting', 'Hosting Channels')}
|
|
||||||
</p>);
|
|
||||||
|
|
||||||
// Hosting Channels Content
|
|
||||||
for (const channel of channels) {
|
|
||||||
simplebarContentChildren.push(<a
|
|
||||||
class="tw-block tw-border-radius-small tw-full-width ffz-interactable ffz-interactable--default ffz-interactable--hover-enabled tw-interactive"
|
|
||||||
href={`/${channel.login}`}
|
|
||||||
onClick={e => this.parent.hijackUserClick(e, channel.login, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
|
|
||||||
>
|
|
||||||
<div class="tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1 tw-mg-y-05">
|
|
||||||
<div class="ffz-channel-avatar">
|
|
||||||
<img src={channel.profileImageURL} alt={channel.displayName} />
|
|
||||||
</div>
|
|
||||||
<p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">
|
|
||||||
{channel.displayName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</a>);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hostMenu = (<div class="ffz-host-menu ffz-balloon tw-block">
|
|
||||||
<div class="tw-border tw-elevation-1 tw-border-radius-small tw-c-background-base tw-pd-05">
|
|
||||||
<div class="scrollable-area" data-simplebar>
|
|
||||||
{simplebarContentChildren}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
|
|
||||||
const root = (document.body.querySelector('#root>div') || document.body);
|
|
||||||
root.appendChild(this.hostMenu);
|
|
||||||
|
|
||||||
this.hostMenuPopper = createPopper(
|
|
||||||
makeReference(event.clientX - 60, event.clientY - 60),
|
|
||||||
this.hostMenu,
|
|
||||||
{
|
|
||||||
placement: 'bottom-start',
|
|
||||||
modifiers: {
|
|
||||||
flip: {
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.hostMenuBuffer = Date.now() + 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChannelCard(inst) {
|
|
||||||
const card = this.fine.getChildNode(inst);
|
|
||||||
|
|
||||||
if ( ! card )
|
|
||||||
return;
|
|
||||||
|
|
||||||
const login = inst.props.channelLogin,
|
|
||||||
hosting = inst.props.channelLinkTo && inst.props.channelLinkTo.state.content === 'live_host' && this.hosts && this.hosts[login];
|
|
||||||
|
|
||||||
if ( hosting && this.settings.get('directory.following.group-hosts') ) {
|
|
||||||
const host_data = this.hosts[login];
|
|
||||||
|
|
||||||
const title_link = card.querySelector('a[data-test-selector="preview-card-titles__primary-link"]'),
|
|
||||||
thumbnail_link = card.querySelector('a[data-a-target="preview-card-image-link"]');
|
|
||||||
|
|
||||||
if ( title_link )
|
|
||||||
title_link.addEventListener('click', this.showHostMenu.bind(this, inst, host_data));
|
|
||||||
|
|
||||||
if ( thumbnail_link )
|
|
||||||
thumbnail_link.addEventListener('click', this.showHostMenu.bind(this, inst, host_data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,7 +48,6 @@ export default class Directory extends SiteModule {
|
||||||
this.inject('i18n');
|
this.inject('i18n');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
|
||||||
//this.inject(Following);
|
|
||||||
this.inject(Game);
|
this.inject(Game);
|
||||||
|
|
||||||
this.DirectoryCard = this.elemental.define(
|
this.DirectoryCard = this.elemental.define(
|
||||||
|
|
|
@ -1,379 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Host Button
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
|
||||||
import {get} from 'utilities/object';
|
|
||||||
import {createElement} from 'utilities/dom';
|
|
||||||
|
|
||||||
const HOST_ERRORS = {
|
|
||||||
COMMAND_EXECUTION: {
|
|
||||||
key: 'command-execution',
|
|
||||||
text: 'There was an error executing the host command. Please try again later.',
|
|
||||||
},
|
|
||||||
CHAT_CONNECTION: {
|
|
||||||
key: 'chat-connection',
|
|
||||||
text: 'There was an issue connecting to chat. Please try again later.',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class HostButton extends Module {
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
|
|
||||||
this.should_enable = true;
|
|
||||||
|
|
||||||
this.inject('site');
|
|
||||||
this.inject('site.fine');
|
|
||||||
this.inject('site.chat');
|
|
||||||
this.inject('site.twitch_data');
|
|
||||||
this.inject('i18n');
|
|
||||||
this.inject('metadata');
|
|
||||||
this.inject('settings');
|
|
||||||
|
|
||||||
this.settings.add('metadata.host-button', {
|
|
||||||
default: true,
|
|
||||||
|
|
||||||
ui: {
|
|
||||||
path: 'Channel > Metadata >> Player',
|
|
||||||
title: 'Host Button',
|
|
||||||
description: 'Show a host button with the current hosted channel in the tooltip.',
|
|
||||||
component: 'setting-check-box'
|
|
||||||
},
|
|
||||||
|
|
||||||
changed: () => {
|
|
||||||
const ffz_user = this.site.getUser(),
|
|
||||||
userLogin = ffz_user && ffz_user.login;
|
|
||||||
|
|
||||||
if (userLogin)
|
|
||||||
this.joinChannel(userLogin);
|
|
||||||
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
isChannelHosted(channelLogin) {
|
|
||||||
return this._last_hosted_channel === channelLogin;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendHostUnhostCommand(channel) {
|
|
||||||
if (!this._chat_con) {
|
|
||||||
this._host_error = HOST_ERRORS.CHAT_CONNECTION;
|
|
||||||
this._host_updating = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ffz_user = this.site.getUser(),
|
|
||||||
userLogin = ffz_user && ffz_user.login;
|
|
||||||
|
|
||||||
const commandData = {channel: userLogin, username: channel};
|
|
||||||
|
|
||||||
this._host_updating = true;
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
|
|
||||||
this._host_feedback = setTimeout(() => {
|
|
||||||
if (this._last_hosted_channel === null) {
|
|
||||||
this._host_error = HOST_ERRORS.COMMAND_EXECUTION;
|
|
||||||
this._host_updating = false;
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
if (this.isChannelHosted(channel)) {
|
|
||||||
this._chat_con.commands.unhost.execute(commandData);
|
|
||||||
} else {
|
|
||||||
this._chat_con.commands.host.execute(commandData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
joinChannel(channel) {
|
|
||||||
if (this._chat_con) {
|
|
||||||
if (this.settings.get('metadata.host-button') && !this._chat_con.session.channelstate[`#${channel}`]) {
|
|
||||||
this._chat_con.joinChannel(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hookIntoChatConnection(inst) {
|
|
||||||
const userLogin = inst.props.currentUserLogin;
|
|
||||||
|
|
||||||
if (this._chat_con) {
|
|
||||||
this.joinChannel(userLogin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.on('tmi:host', e => {
|
|
||||||
if (e.channel.substring(1) !== userLogin) return;
|
|
||||||
|
|
||||||
clearTimeout(this._host_feedback);
|
|
||||||
this._host_error = false;
|
|
||||||
this._last_hosted_channel = e.target;
|
|
||||||
|
|
||||||
this._host_updating = false;
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on('tmi:unhost', e => {
|
|
||||||
if (e.channel.substring(1) !== userLogin) return;
|
|
||||||
|
|
||||||
clearTimeout(this._host_feedback);
|
|
||||||
this._host_error = false;
|
|
||||||
this._last_hosted_channel = null;
|
|
||||||
|
|
||||||
this._host_updating = false;
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
});
|
|
||||||
|
|
||||||
const chatServiceClient = inst.client;
|
|
||||||
|
|
||||||
this._chat_con = chatServiceClient;
|
|
||||||
if (this.settings.get('metadata.host-button'))
|
|
||||||
this.joinChannel(userLogin);
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnable() {
|
|
||||||
this.on('i18n:update', () => this.metadata.updateMetadata('host'));
|
|
||||||
|
|
||||||
this.metadata.definitions.host = {
|
|
||||||
order: 150,
|
|
||||||
border: true,
|
|
||||||
button: true,
|
|
||||||
fade_in: true,
|
|
||||||
modview: true,
|
|
||||||
|
|
||||||
disabled: () => this._host_updating || this._host_error,
|
|
||||||
|
|
||||||
click: data => {
|
|
||||||
if ( data.channel )
|
|
||||||
this.sendHostUnhostCommand(data.channel.login);
|
|
||||||
},
|
|
||||||
|
|
||||||
popup: async (data, tip) => {
|
|
||||||
const vue = this.resolve('vue'),
|
|
||||||
_host_options_vue = import(/* webpackChunkName: "host-options" */ './host-options.vue'),
|
|
||||||
_autoHosts = this.fetchAutoHosts(),
|
|
||||||
_autoHostSettings = this.fetchAutoHostSettings();
|
|
||||||
|
|
||||||
const [, host_options_vue, autoHosts, autoHostSettings] = await Promise.all([vue.enable(), _host_options_vue, _autoHosts, _autoHostSettings]);
|
|
||||||
|
|
||||||
this._auto_host_tip = tip;
|
|
||||||
tip.element.classList.remove('tw-pd-1');
|
|
||||||
tip.element.classList.add('ffz-balloon--lg');
|
|
||||||
vue.component('host-options', host_options_vue.default);
|
|
||||||
return this.buildAutoHostMenu(vue, autoHosts, autoHostSettings, data.channel);
|
|
||||||
},
|
|
||||||
|
|
||||||
label: data => {
|
|
||||||
const ffz_user = this.site.getUser();
|
|
||||||
|
|
||||||
if ( ! this.settings.get('metadata.host-button') || ! ffz_user || ! data.channel || data.channel.login === ffz_user.login )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( data.channel.video && ! this.isChannelHosted(data.channel.login) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( this._host_updating )
|
|
||||||
return this.i18n.t('metadata.host-button.updating', 'Updating...');
|
|
||||||
|
|
||||||
return (this._last_hosted_channel && this.isChannelHosted(data.channel && data.channel.login))
|
|
||||||
? this.i18n.t('metadata.host-button.unhost', 'Unhost')
|
|
||||||
: this.i18n.t('metadata.host-button.host', 'Host');
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip: () => {
|
|
||||||
if (this._host_error) {
|
|
||||||
return this.i18n.t(
|
|
||||||
`metadata.host-button.tooltip.error.${this._host_error.key}`,
|
|
||||||
this._host_error.text);
|
|
||||||
} else {
|
|
||||||
return this.i18n.t('metadata.host-button.tooltip',
|
|
||||||
'Currently hosting: {channel}',
|
|
||||||
{
|
|
||||||
channel: this._last_hosted_channel || this.i18n.t('metadata.host-button.tooltip.none', 'None')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.metadata.updateMetadata('host');
|
|
||||||
|
|
||||||
this.chat.ChatService.ready((cls, instances) => {
|
|
||||||
for(const inst of instances)
|
|
||||||
this.hookIntoChatConnection(inst);
|
|
||||||
})
|
|
||||||
|
|
||||||
this.chat.ChatService.on('mount', this.hookIntoChatConnection, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildAutoHostMenu(vue, hosts, autoHostSettings, data) {
|
|
||||||
this._current_channel_id = data.id;
|
|
||||||
this.activeTab = this.activeTab || 'auto-host';
|
|
||||||
|
|
||||||
const vueEl = new vue.Vue({
|
|
||||||
el: createElement('div'),
|
|
||||||
render: h => this.vueHostMenu = h('host-options', {
|
|
||||||
hosts,
|
|
||||||
autoHostSettings,
|
|
||||||
activeTab: this.activeTab,
|
|
||||||
|
|
||||||
addedToHosts: this.currentRoomInHosts(),
|
|
||||||
addToAutoHosts: () => this.addCurrentRoomToHosts(),
|
|
||||||
rearrangeHosts: event => this.rearrangeHosts(event.oldIndex, event.newIndex),
|
|
||||||
removeFromHosts: event => this.removeUserFromHosts(event),
|
|
||||||
setActiveTab: tab => {
|
|
||||||
this.vueHostMenu.data.activeTab = this.activeTab = tab;
|
|
||||||
},
|
|
||||||
updatePopper: () => {
|
|
||||||
if (this._auto_host_tip) this._auto_host_tip.update();
|
|
||||||
},
|
|
||||||
updateCheckbox: e => {
|
|
||||||
const t = e.target;
|
|
||||||
let setting = t.dataset.setting,
|
|
||||||
state = t.checked;
|
|
||||||
|
|
||||||
if ( setting === 'enabled' )
|
|
||||||
setting = 'isEnabled';
|
|
||||||
else if ( setting === 'teamHost' )
|
|
||||||
setting = 'willAutohostTeam';
|
|
||||||
else if ( setting === 'strategy' )
|
|
||||||
state = state ? 'RANDOM' : 'ORDERED';
|
|
||||||
else if ( setting === 'deprioritizeVodcast' ) {
|
|
||||||
setting = 'willPrioritizeAutohost';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateAutoHostSetting(setting, state);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
return vueEl.$el;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchAutoHosts() {
|
|
||||||
const user = this.site.getUser();
|
|
||||||
if ( ! user )
|
|
||||||
return;
|
|
||||||
|
|
||||||
const result = await this.twitch_data.queryApollo(
|
|
||||||
await import(/* webpackChunkName: 'host-options' */ './autohost_list.gql'),
|
|
||||||
{
|
|
||||||
id: user.id
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fetchPolicy: 'network-only'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.autoHosts = get('data.user.autohostChannels.nodes', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchAutoHostSettings() {
|
|
||||||
const user = this.site.getUser();
|
|
||||||
if ( ! user )
|
|
||||||
return;
|
|
||||||
|
|
||||||
const result = await this.twitch_data.queryApollo(
|
|
||||||
await import(/* webpackChunkName: 'host-options' */ './autohost_settings.gql'),
|
|
||||||
{
|
|
||||||
id: user.id
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fetchPolicy: 'network-only'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.autoHostSettings = get('data.user.autohostSettings', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
queueHostUpdate() {
|
|
||||||
if (this._host_update_timer) clearTimeout(this._host_update_timer);
|
|
||||||
|
|
||||||
this._host_update_timer = setTimeout(() => {
|
|
||||||
this._host_update_timer = undefined;
|
|
||||||
this.updateAutoHosts(this.autoHosts);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
rearrangeHosts(oldIndex, newIndex) {
|
|
||||||
const host = this.autoHosts.splice(oldIndex, 1)[0];
|
|
||||||
this.autoHosts.splice(newIndex, 0, host);
|
|
||||||
|
|
||||||
this.queueHostUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRoomInHosts() {
|
|
||||||
return this.getAutoHostIDs(this.autoHosts).includes(this._current_channel_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
addCurrentRoomToHosts() {
|
|
||||||
const newHosts = this.autoHosts.slice(0);
|
|
||||||
newHosts.push({ id: this._current_channel_id});
|
|
||||||
|
|
||||||
this.updateAutoHosts(newHosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUserFromHosts(event) {
|
|
||||||
const id = event.target.closest('.ffz--host-user').dataset.id;
|
|
||||||
|
|
||||||
const newHosts = [];
|
|
||||||
for (let i = 0; i < this.autoHosts.length; i++) {
|
|
||||||
if (this.autoHosts[i].id != id) newHosts.push(this.autoHosts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateAutoHosts(newHosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAutoHostIDs(hosts) { // eslint-disable-line class-methods-use-this
|
|
||||||
const ids = [];
|
|
||||||
if (hosts) {
|
|
||||||
for (let i = 0; i < hosts.length; i++) {
|
|
||||||
ids.push(hosts[i].id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoHosts(newHosts) {
|
|
||||||
const user = this.site.getUser();
|
|
||||||
if ( ! user )
|
|
||||||
return;
|
|
||||||
|
|
||||||
const autoHosts = this.getAutoHostIDs(newHosts);
|
|
||||||
|
|
||||||
const result = await this.twitch_data.mutate({
|
|
||||||
mutation: await import(/* webpackChunkName: 'host-options' */ './autohost_list_mutate.gql'),
|
|
||||||
variables: {
|
|
||||||
userID: user.id,
|
|
||||||
channelIDs: autoHosts
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.autoHosts = get('data.setAutohostChannels.user.autohostChannels.nodes', result);
|
|
||||||
if (this.vueHostMenu) {
|
|
||||||
this.vueHostMenu.data.hosts = this.autoHosts;
|
|
||||||
this.vueHostMenu.data.addedToHosts = this.currentRoomInHosts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoHostSetting(setting, newValue) {
|
|
||||||
const user = this.site.getUser();
|
|
||||||
if ( ! user )
|
|
||||||
return;
|
|
||||||
|
|
||||||
const result = await this.twitch_data.mutate({
|
|
||||||
mutation: await import(/* webpackChunkName: 'host-options' */ './autohost_settings_mutate.gql'),
|
|
||||||
variables: {
|
|
||||||
userID: user.id,
|
|
||||||
[setting]: newValue
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.autoHostSettings = get('data.updateAutohostSettings.user.autohostSettings', result);
|
|
||||||
if (this.vueHostMenu) {
|
|
||||||
this.vueHostMenu.data.autoHostSettings = this.autoHostSettings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -172,7 +172,7 @@ export default class Layout extends Module {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('layout.portrait-extra-height', {
|
this.settings.add('layout.portrait-extra-height', {
|
||||||
requires: ['context.new_channel', 'context.squad_bar', 'context.hosting', 'context.ui.theatreModeEnabled', 'player.theatre.no-whispers', 'whispers.show', 'layout.minimal-navigation'],
|
requires: ['context.new_channel', 'context.squad_bar', /*'context.hosting',*/ 'context.ui.theatreModeEnabled', 'player.theatre.no-whispers', 'whispers.show', 'layout.minimal-navigation'],
|
||||||
process(ctx) {
|
process(ctx) {
|
||||||
let height = 0;
|
let height = 0;
|
||||||
if ( ctx.get('context.ui.theatreModeEnabled') ) {
|
if ( ctx.get('context.ui.theatreModeEnabled') ) {
|
||||||
|
@ -192,8 +192,8 @@ export default class Layout extends Module {
|
||||||
|
|
||||||
height += ctx.get('context.new_channel') ? 1 : 5;
|
height += ctx.get('context.new_channel') ? 1 : 5;
|
||||||
|
|
||||||
if ( ctx.get('context.hosting') )
|
/*if ( ctx.get('context.hosting') )
|
||||||
height += 4;
|
height += 4;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
|
|
@ -196,7 +196,7 @@ export default class Player extends PlayerBase {
|
||||||
|
|
||||||
|
|
||||||
checkCarousel(inst) {
|
checkCarousel(inst) {
|
||||||
if ( this.settings.get('channel.hosting.enable') )
|
/*if ( this.settings.get('channel.hosting.enable') )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( inst.props?.playerType === 'channel_home_carousel' ) {
|
if ( inst.props?.playerType === 'channel_home_carousel' ) {
|
||||||
|
@ -211,7 +211,7 @@ export default class Player extends PlayerBase {
|
||||||
events = inst.props.playerEvents;
|
events = inst.props.playerEvents;
|
||||||
|
|
||||||
this.stopPlayer(player, events, inst);
|
this.stopPlayer(player, events, inst);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
@import 'fixes';
|
@import 'fixes';
|
||||||
|
|
||||||
@import 'host_options';
|
//@import 'host_options';
|
||||||
@import 'featured_follow';
|
@import 'featured_follow';
|
||||||
@import 'mod_card';
|
@import 'mod_card';
|
||||||
@import 'easteregg';
|
@import 'easteregg';
|
Loading…
Add table
Add a link
Reference in a new issue