mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-03 00:18:31 +00:00
4.20.58
* Added: Metadata rendering for Mod View, including stream latency, the host menu, etc. Certain metadata such as up-time is not supported due to Mod View already displaying that information. Closes #984. Closes #950. Closes #862. * Added: Setting to disable the "Hide Stream Info Stripe" button in Mod View to avoid metadata elements moving during interaction. * Added: Re-enable the setting to disable the player speeding up. The new implementation overrides a property on the underlying `<video>` rather than directly interacting with Twitch's player code. * Fixed: Channel Points UI getting cut off when chat width is set smaller than the default. Closes #965 * API Added: Metadata definitions now have a `modview` attribute for opting into inclusion on mod view pages.
This commit is contained in:
parent
046de0bb8a
commit
b337b6abe3
10 changed files with 176 additions and 24 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.20.57",
|
"version": "4.20.58",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -291,6 +291,7 @@ export default class Metadata extends Module {
|
||||||
this.definitions['player-stats'] = {
|
this.definitions['player-stats'] = {
|
||||||
button: true,
|
button: true,
|
||||||
inherit: true,
|
inherit: true,
|
||||||
|
modview: true,
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
return this.settings.get('metadata.player-stats')
|
return this.settings.get('metadata.player-stats')
|
||||||
|
|
|
@ -22,6 +22,7 @@ const CLASSES = {
|
||||||
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels,.side-nav--collapsed .side-nav-section + .side-nav-section:not(.online-friends)',
|
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels,.side-nav--collapsed .side-nav-section + .side-nav-section:not(.online-friends)',
|
||||||
'side-offline-channels': '.ffz--side-nav-card-offline',
|
'side-offline-channels': '.ffz--side-nav-card-offline',
|
||||||
'side-rerun-channels': '.side-nav .ffz--side-nav-card-rerun',
|
'side-rerun-channels': '.side-nav .ffz--side-nav-card-rerun',
|
||||||
|
'modview-hide-info': '.tw-flex.modview-player-widget__hide-stream-info',
|
||||||
|
|
||||||
'community-highlights': '.community-highlight-stack__card',
|
'community-highlights': '.community-highlight-stack__card',
|
||||||
|
|
||||||
|
@ -62,6 +63,16 @@ export default class CSSTweaks extends Module {
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
|
|
||||||
|
this.settings.add('metadata.modview.hide-info', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Channel > Metadata >> Mod View',
|
||||||
|
title: 'Hide "Hide Stream Info Stripe" button.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
},
|
||||||
|
changed: val => this.toggleHide('modview-hide-info', val)
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('metadata.viewers.no-native', {
|
this.settings.add('metadata.viewers.no-native', {
|
||||||
requires: ['metadata.viewers'],
|
requires: ['metadata.viewers'],
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -420,6 +431,7 @@ export default class CSSTweaks extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
this.toggleHide('modview-hide-info', this.settings.get('metadata.modview.hide-info'));
|
||||||
this.toggleHide('side-nav-viewers', this.settings.get('layout.side-nav.hide-viewers'));
|
this.toggleHide('side-nav-viewers', this.settings.get('layout.side-nav.hide-viewers'));
|
||||||
this.toggle('hide-native-uptime', this.settings.get('metadata.uptime.no-native'));
|
this.toggle('hide-native-uptime', this.settings.get('metadata.uptime.no-native'));
|
||||||
this.toggle('hide-native-viewers', this.settings.get('metadata.viewers.no-native'));
|
this.toggle('hide-native-viewers', this.settings.get('metadata.viewers.no-native'));
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
min-width: unset !important;
|
min-width: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reward-center__content {
|
||||||
|
width: calc(var(--ffz-chat-width) - 2rem) !important;
|
||||||
|
}
|
||||||
|
|
||||||
body .whispers--theatre-mode.whispers--right-column-expanded-beside {
|
body .whispers--theatre-mode.whispers--right-column-expanded-beside {
|
||||||
right: var(--ffz-chat-width);
|
right: var(--ffz-chat-width);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ export default class FeaturedFollow extends Module {
|
||||||
this.metadata.definitions.following = {
|
this.metadata.definitions.following = {
|
||||||
order: 150,
|
order: 150,
|
||||||
button: true,
|
button: true,
|
||||||
|
modview: true,
|
||||||
|
|
||||||
popup: async (data, tip, refresh_fn, add_callback) => {
|
popup: async (data, tip, refresh_fn, add_callback) => {
|
||||||
const vue = this.resolve('vue'),
|
const vue = this.resolve('vue'),
|
||||||
|
|
|
@ -59,6 +59,7 @@ export default class HostButton extends Module {
|
||||||
border: true,
|
border: true,
|
||||||
button: true,
|
button: true,
|
||||||
fade_in: true,
|
fade_in: true,
|
||||||
|
modview: true,
|
||||||
|
|
||||||
disabled: () => this._host_updating || this._host_error,
|
disabled: () => this._host_updating || this._host_error,
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import { Color } from 'utilities/color';
|
|
||||||
import {debounce} from 'utilities/object';
|
import {debounce} from 'utilities/object';
|
||||||
import {createElement, ClickOutside, setChildren} from 'utilities/dom';
|
|
||||||
|
|
||||||
|
|
||||||
export default class ModView extends Module {
|
export default class ModView extends Module {
|
||||||
|
@ -28,6 +26,7 @@ export default class ModView extends Module {
|
||||||
this.should_enable = true;
|
this.should_enable = true;
|
||||||
|
|
||||||
this._cached_channel = null;
|
this._cached_channel = null;
|
||||||
|
this._cached_id = null;
|
||||||
|
|
||||||
this.Root = this.elemental.define(
|
this.Root = this.elemental.define(
|
||||||
'mod-view-root', '.moderation-view-page',
|
'mod-view-root', '.moderation-view-page',
|
||||||
|
@ -38,10 +37,11 @@ export default class ModView extends Module {
|
||||||
this.ModInfoBar = this.elemental.define(
|
this.ModInfoBar = this.elemental.define(
|
||||||
'mod-info-bar', '.modview-player-widget__stream-info .simplebar-content',
|
'mod-info-bar', '.modview-player-widget__stream-info .simplebar-content',
|
||||||
['mod-view'],
|
['mod-view'],
|
||||||
{childNodes: true, subtree: true}, 1
|
{childNodes: true, subtree: true}, 1, 30000, false
|
||||||
);
|
);
|
||||||
|
|
||||||
this.checkRoot = debounce(this.checkRoot, 250);
|
this.checkRoot = debounce(this.checkRoot, 250);
|
||||||
|
this.checkBar = debounce(this.checkBar, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -81,6 +81,10 @@ export default class ModView extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkBar() {
|
||||||
|
this.ModInfoBar.clean();
|
||||||
|
}
|
||||||
|
|
||||||
checkRoot() {
|
checkRoot() {
|
||||||
this.Root.each(el => this.updateRoot(el));
|
this.Root.each(el => this.updateRoot(el));
|
||||||
}
|
}
|
||||||
|
@ -95,8 +99,9 @@ export default class ModView extends Module {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( channel?.id && this._cached_channel != channel.id ) {
|
if ( channel?.id && this._cached_id != channel.id ) {
|
||||||
this._cached_channel = channel.id;
|
this._cached_id = channel.id;
|
||||||
|
this._cached_channel = channel;
|
||||||
this.updateSubscription(channel.login);
|
this.updateSubscription(channel.login);
|
||||||
|
|
||||||
this.getChannelColor(el, channel.id).then(color => {
|
this.getChannelColor(el, channel.id).then(color => {
|
||||||
|
@ -171,6 +176,7 @@ export default class ModView extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRoot() {
|
removeRoot() {
|
||||||
|
this._cached_id = null;
|
||||||
this._cached_channel = null;
|
this._cached_channel = null;
|
||||||
this.updateSubscription();
|
this.updateSubscription();
|
||||||
this.channel.updateChannelColor();
|
this.channel.updateChannelColor();
|
||||||
|
@ -196,7 +202,7 @@ export default class ModView extends Module {
|
||||||
title = bcast?.title,
|
title = bcast?.title,
|
||||||
game = bcast?.game;
|
game = bcast?.game;
|
||||||
|
|
||||||
if ( channel?.id && channel.id != this._cached_channel )
|
if ( channel?.id && channel.id != this._cached_id )
|
||||||
this.checkRoot();
|
this.checkRoot();
|
||||||
|
|
||||||
if ( title != el._cached_title || game?.id != el._cached_game ) {
|
if ( title != el._cached_title || game?.id != el._cached_game ) {
|
||||||
|
@ -209,6 +215,16 @@ export default class ModView extends Module {
|
||||||
title
|
title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( container ) {
|
||||||
|
if ( ! container._ffz_cont ) {
|
||||||
|
const e = container._ffz_cont = container.querySelector('.modview-player-widget__viewcount');
|
||||||
|
if ( e )
|
||||||
|
e.classList.add('ffz--mod-tray');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateMetadata(container);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeBar(el) {
|
removeBar(el) {
|
||||||
|
@ -218,18 +234,68 @@ export default class ModView extends Module {
|
||||||
title: null
|
title: null
|
||||||
});
|
});
|
||||||
|
|
||||||
if ( el._ffz_cont )
|
const container = el.closest('.modview-player-widget__stream-info');
|
||||||
el._ffz_cont.classList.remove('ffz--meta-tray');
|
if ( ! container )
|
||||||
|
return;
|
||||||
|
|
||||||
el._ffz_cont = null;
|
if ( container._ffz_cont )
|
||||||
if ( el._ffz_meta_timers ) {
|
container._ffz_cont.classList.remove('ffz--mod-tray');
|
||||||
for(const val of Object.values(el._ffz_meta_timers))
|
|
||||||
|
container._ffz_cont = null;
|
||||||
|
if ( container._ffz_meta_timers ) {
|
||||||
|
for(const val of Object.values(container._ffz_meta_timers))
|
||||||
clearTimeout(val);
|
clearTimeout(val);
|
||||||
|
|
||||||
el._ffz_meta_timers = null;
|
container._ffz_meta_timers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
el._ffz_update = null;
|
container._ffz_update = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMetadata(el, keys) {
|
||||||
|
const cont = el._ffz_cont,
|
||||||
|
channel = this._cached_channel;
|
||||||
|
//root = this.fine.getReactInstance(el);
|
||||||
|
|
||||||
|
/*let channel = null, state = root?.return?.memoizedState, i = 0;
|
||||||
|
while(state != null && channel == null && i < 50 ) {
|
||||||
|
state = state?.next;
|
||||||
|
channel = state?.memoizedState?.current?.previousData?.result?.data?.channel;
|
||||||
|
i++;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if ( ! cont || ! document.contains(cont) ) {
|
||||||
|
this.checkBar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! channel?.id )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( ! keys )
|
||||||
|
keys = this.metadata.keys;
|
||||||
|
else if ( ! Array.isArray(keys) )
|
||||||
|
keys = [keys];
|
||||||
|
|
||||||
|
const timers = el._ffz_meta_timers = el._ffz_meta_timers || {},
|
||||||
|
refresh_fn = key => this.updateMetadata(el, key),
|
||||||
|
data = {
|
||||||
|
channel: {
|
||||||
|
id: channel.id,
|
||||||
|
login: channel.login,
|
||||||
|
display_name: channel.displayName
|
||||||
|
},
|
||||||
|
el,
|
||||||
|
getViewerCount: () => 0,
|
||||||
|
getUserSelfImmediate: () => null,
|
||||||
|
getUserSelf: () => null,
|
||||||
|
getBroadcastID: () => null
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const key of keys)
|
||||||
|
if ( this.metadata.definitions[key].modview )
|
||||||
|
this.metadata.renderLegacy(key, data, cont, timers, refresh_fn);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {createElement, on, off} from 'utilities/dom';
|
import {createElement, on, off} from 'utilities/dom';
|
||||||
import {debounce} from 'utilities/object';
|
import {debounce} from 'utilities/object';
|
||||||
|
import { IS_FIREFOX } from 'src/utilities/constants';
|
||||||
|
|
||||||
export const PLAYER_ROUTES = [
|
export const PLAYER_ROUTES = [
|
||||||
'front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos',
|
'front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos',
|
||||||
|
@ -15,6 +16,11 @@ export const PLAYER_ROUTES = [
|
||||||
'mod-view', 'user-home'
|
'mod-view', 'user-home'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const HAS_PITCH = (() => {
|
||||||
|
const el = createElement('video');
|
||||||
|
return el.preservesPitch != null || el.mozPreservesPitch != null
|
||||||
|
})();
|
||||||
|
|
||||||
const HAS_COMPRESSOR = window.AudioContext && window.DynamicsCompressorNode != null;
|
const HAS_COMPRESSOR = window.AudioContext && window.DynamicsCompressorNode != null;
|
||||||
|
|
||||||
const STYLE_VALIDATOR = createElement('span');
|
const STYLE_VALIDATOR = createElement('span');
|
||||||
|
@ -217,9 +223,6 @@ export default class Player extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// This is currently broken due to changes Twitch has made in the player
|
|
||||||
// backend. Removing it for now to avoid user confusion.
|
|
||||||
this.settings.add('player.allow-catchup', {
|
this.settings.add('player.allow-catchup', {
|
||||||
default: true,
|
default: true,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -229,11 +232,8 @@ export default class Player extends Module {
|
||||||
component: 'setting-check-box'
|
component: 'setting-check-box'
|
||||||
},
|
},
|
||||||
|
|
||||||
changed: val => {
|
changed: () => this.updatePlaybackRates()
|
||||||
for(const inst of this.Player.instances)
|
});
|
||||||
this.updateAutoPlaybackRate(inst, val);
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
this.settings.add('player.mute-click', {
|
this.settings.add('player.mute-click', {
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -809,16 +809,19 @@ export default class Player extends Module {
|
||||||
|
|
||||||
this.updateGUI(inst);
|
this.updateGUI(inst);
|
||||||
this.compressPlayer(inst);
|
this.compressPlayer(inst);
|
||||||
|
this.updatePlaybackRate(inst);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Player.on('mount', inst => {
|
this.Player.on('mount', inst => {
|
||||||
this.updateGUI(inst);
|
this.updateGUI(inst);
|
||||||
this.compressPlayer(inst);
|
this.compressPlayer(inst);
|
||||||
|
this.updatePlaybackRate(inst);
|
||||||
});
|
});
|
||||||
this.Player.on('update', inst => {
|
this.Player.on('update', inst => {
|
||||||
this.updateGUI(inst);
|
this.updateGUI(inst);
|
||||||
this.compressPlayer(inst);
|
this.compressPlayer(inst);
|
||||||
|
this.updatePlaybackRate(inst);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Player.on('unmount', inst => {
|
this.Player.on('unmount', inst => {
|
||||||
|
@ -1058,7 +1061,7 @@ export default class Player extends Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let icon, tip, extra, btn, cont = container.querySelector('.ffz--player-comp');
|
let icon, tip, extra, ff_el, btn, cont = container.querySelector('.ffz--player-comp');
|
||||||
if ( ! has_comp ) {
|
if ( ! has_comp ) {
|
||||||
if ( cont )
|
if ( cont )
|
||||||
cont.remove();
|
cont.remove();
|
||||||
|
@ -1083,6 +1086,7 @@ export default class Player extends Module {
|
||||||
<div>
|
<div>
|
||||||
{tip = (<div class="ffz--p-tip" />)}
|
{tip = (<div class="ffz--p-tip" />)}
|
||||||
{extra = (<div class="ffz--p-extra tw-pd-t-05 ffz--tooltip-explain" />)}
|
{extra = (<div class="ffz--p-extra tw-pd-t-05 ffz--tooltip-explain" />)}
|
||||||
|
{ff_el = IS_FIREFOX ? (<div class="ffz--p-ff tw-pd-t-05 ffz--tooltip-explain" />) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
@ -1107,6 +1111,9 @@ export default class Player extends Module {
|
||||||
|
|
||||||
extra.textContent = this.i18n.t('player.comp_button.help', 'See the FFZ Control Center for details. If audio breaks, please reset the player.');
|
extra.textContent = this.i18n.t('player.comp_button.help', 'See the FFZ Control Center for details. If audio breaks, please reset the player.');
|
||||||
|
|
||||||
|
if ( ff_el )
|
||||||
|
ff_el.textContent += `\n${this.i18n.t('player.comp_button.firefox', 'Playback Speed controls will not function for Firefox users when the Compressor has been enabled.')}`;
|
||||||
|
|
||||||
icon.classList.toggle('ffz-i-comp-on', comp_active);
|
icon.classList.toggle('ffz-i-comp-on', comp_active);
|
||||||
icon.classList.toggle('ffz-i-comp-off', ! comp_active);
|
icon.classList.toggle('ffz-i-comp-off', ! comp_active);
|
||||||
btn.disabled = ! can_apply;
|
btn.disabled = ! can_apply;
|
||||||
|
@ -1234,6 +1241,52 @@ export default class Player extends Module {
|
||||||
comp.release.value = this.settings.get('player.compressor.release');
|
comp.release.value = this.settings.get('player.compressor.release');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePlaybackRates() {
|
||||||
|
for(const inst of this.Player.instances)
|
||||||
|
this.updatePlaybackRate(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePlaybackRate(inst) {
|
||||||
|
const video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video ||
|
||||||
|
inst.props.mediaPlayerInstance?.core?.mediaSinkManager?.video;
|
||||||
|
|
||||||
|
if ( ! video.setFFZPlaybackRate )
|
||||||
|
this.installPlaybackRate(video);
|
||||||
|
|
||||||
|
video.setFFZPlaybackRate(video.playbackRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
installPlaybackRate(video) {
|
||||||
|
if ( video.setFFZPlaybackRate )
|
||||||
|
return;
|
||||||
|
|
||||||
|
let pbrate = video.playbackRate;
|
||||||
|
|
||||||
|
const t = this,
|
||||||
|
installProperty = () => {
|
||||||
|
if ( t.settings.get('player.allow-catchup') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object.defineProperty(video, 'playbackRate', {
|
||||||
|
configurable: true,
|
||||||
|
get() {
|
||||||
|
return pbrate;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if ( val === 1 || val < 1 || val >= 1.1 )
|
||||||
|
video.setFFZPlaybackRate(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
video.setFFZPlaybackRate = rate => {
|
||||||
|
delete video.playbackRate;
|
||||||
|
pbrate = rate;
|
||||||
|
video.playbackRate = rate;
|
||||||
|
installProperty();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
addPiPButton(inst, tries = 0) {
|
addPiPButton(inst, tries = 0) {
|
||||||
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
|
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
|
||||||
|
@ -1506,6 +1559,7 @@ export default class Player extends Module {
|
||||||
muted = player.isMuted();
|
muted = player.isMuted();
|
||||||
new_vid.volume = muted ? 0 : vol;
|
new_vid.volume = muted ? 0 : vol;
|
||||||
new_vid.playsInline = true;
|
new_vid.playsInline = true;
|
||||||
|
this.installPlaybackRate(new_vid);
|
||||||
video.replaceWith(new_vid);
|
video.replaceWith(new_vid);
|
||||||
player.attachHTMLVideoElement(new_vid);
|
player.attachHTMLVideoElement(new_vid);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -60,6 +60,16 @@
|
||||||
margin-bottom: -.7rem !important;
|
margin-bottom: -.7rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz--mod-tray {
|
||||||
|
margin-right: -1rem;
|
||||||
|
|
||||||
|
& > :not(.ffz-stat) {
|
||||||
|
order: 1;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.ffz--meta-tray:not(.tw-flex-wrap) {
|
.ffz--meta-tray:not(.tw-flex-wrap) {
|
||||||
& > *:not(.ffz-stat) {
|
& > *:not(.ffz-stat) {
|
||||||
order: 500;
|
order: 500;
|
||||||
|
|
|
@ -152,10 +152,13 @@ export default class Elemental extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
listen(inst) {
|
listen(inst, ensure_live = true) {
|
||||||
if ( this._watching.has(inst) )
|
if ( this._watching.has(inst) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( ensure_live )
|
||||||
|
this._timer = Date.now();
|
||||||
|
|
||||||
this._watching.add(inst);
|
this._watching.add(inst);
|
||||||
this._updateLiveWatching();
|
this._updateLiveWatching();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue