1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-05 10:38:30 +00:00

4.0.0-rc18.2

More performance!

* Changed: Push the chat scroll to bottom into a new animation frame to avoid costly recalculations as much as possible.
* Fixed: Runaway performance issue when using FontAwesome icons for in-line chat actions due to an overabundance of CSS.
* Fixed: Emitting updated events for settings that haven't changed, resulting in frequent re-rendering of all chat lines.
This commit is contained in:
SirStendec 2019-04-30 15:18:29 -04:00
parent 918a5fbb13
commit 8bc25b8d5f
7 changed files with 182 additions and 15 deletions

View file

@ -149,7 +149,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
FrankerFaceZ.Logger = Logger;
const VER = FrankerFaceZ.version_info = {
major: 4, minor: 0, revision: 0, extra: '-rc18.1',
major: 4, minor: 0, revision: 0, extra: '-rc18.2',
commit: __git_commit__,
build: __webpack_hash__,
toString: () =>

View file

@ -300,6 +300,7 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-users--color-regex', {
requires: ['chat.filtering.highlight-basic-users'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-users');
if ( ! val || ! val.length )
@ -358,7 +359,8 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-users-blocked--regex', {
requires: ['chat.filtering.highlight-basic-blocked'],
requires: ['chat.filtering.highlight-basic-users-blocked'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-users-blocked');
if ( ! val || ! val.length )
@ -407,6 +409,7 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-badges--colors', {
requires: ['chat.filtering.highlight-basic-badges'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-badges');
if ( ! val || ! val.length )
@ -440,6 +443,7 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-badges-blocked--list', {
requires: ['chat.filtering.highlight-basic-badges-blocked'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-badges-blocked');
if ( ! val || ! val.length )
@ -471,6 +475,7 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-terms--color-regex', {
requires: ['chat.filtering.highlight-basic-terms'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-terms');
if ( ! val || ! val.length )
@ -537,6 +542,7 @@ export default class Chat extends Module {
this.settings.add('chat.filtering.highlight-basic-blocked--regex', {
requires: ['chat.filtering.highlight-basic-blocked'],
equals: 'requirements',
process(ctx) {
const val = ctx.get('chat.filtering.highlight-basic-blocked');
if ( ! val || ! val.length )

View file

@ -5,10 +5,47 @@
// ============================================================================
import {EventEmitter} from 'utilities/events';
import {has, get as getter, array_equals} from 'utilities/object';
import {has, get as getter, array_equals, set_equals, map_equals, deep_copy} from 'utilities/object';
import * as DEFINITIONS from './types';
/**
* Perform a basic check of a setting's requirements to see if they changed.
* @param {Object} definition
* @param {Map} cache
* @param {Map} old_cache
* @returns Whether or not they changed.
*/
function compare_requirements(definition, cache, old_cache) {
if ( ! definition || ! Array.isArray(definition.requires) )
return false;
for(const req of definition.requires) {
const old_value = old_cache.get(req),
new_value = cache.get(req);
if ( typeof old_value !== typeof new_value )
return true;
if ( Array.isArray(old_value) && Array.isArray(new_value) ) {
if ( ! array_equals(new_value, old_value) )
return true;
} else if ( new_value instanceof Set && old_value instanceof Set ) {
if ( ! set_equals(new_value, old_value) )
return true;
} else if( new_value instanceof Map && old_value instanceof Map ) {
if ( ! map_equals(new_value, old_value) )
return true;
} else if ( new_value !== old_value )
return true;
}
return false;
}
/**
* The SettingsContext class provides a context through which to read
@ -34,6 +71,10 @@ export default class SettingsContext extends EventEmitter {
this.manager.__contexts.push(this);
this._context = context || {};
/*this._context_objects = new Set;
if ( context )
this._updateContext(context, undefined, undefined, new Set);*/
this.__cache = new Map;
this.__meta = new Map;
this.__profiles = [];
@ -125,7 +166,29 @@ export default class SettingsContext extends EventEmitter {
new_uses = new_m ? new_m.uses : null,
old_uses = old_m ? old_m.uses : null;
if ( new_value !== old_value ) {
const definition = this.manager.definitions.get(key);
let changed = false;
if ( definition && definition.equals ) {
if ( definition.equals === 'requirements' )
changed = compare_requirements(definition, this.__cache, old_cache);
else if ( typeof definition.equals === 'function' )
changed = ! definition.equals(new_value, old_value, this.__cache, old_cache);
}
else if ( Array.isArray(new_value) && Array.isArray(old_value) )
changed = ! array_equals(new_value, old_value);
else if ( new_value instanceof Set && old_value instanceof Set )
changed = ! set_equals(new_value, old_value);
else if ( new_value instanceof Map && old_value instanceof Map )
changed = ! map_equals(new_value, old_value);
else if ( new_value !== old_value )
changed = true;
if ( changed ) {
this.emit('changed', key, new_value, old_value);
this.emit(`changed:${key}`, new_value, old_value);
}
@ -161,9 +224,68 @@ export default class SettingsContext extends EventEmitter {
}
/*_updateContext(context, prefix, keys, changed) {
if ( ! context || typeof context !== 'object' )
return;
if ( ! keys )
keys = Object.keys(context);
if ( prefix && this._context_objects.has(prefix) ) {
for(const key of Object.keys(this._context) )
if ( key.startsWith(prefix) ) {
const partial_key = key.substr(prefix.length);
if ( ! keys.includes(partial_key) )
keys.push(partial_key);
}
}
if ( prefix )
this._context_objects.add(prefix);
for(const key of keys) {
const full_key = prefix ? `${prefix}${key}` : key,
pref = `${full_key}.`,
old_value = this._context[full_key],
val = context[key];
const keys = val && (typeof val === 'object') && Object.keys(val);
if ( keys && keys.length ) {
this._updateContext(val, pref, keys, changed);
} else if ( this._context_objects.has(pref) ) {
this._updateContext({}, pref, [], changed);
this._context_objects.delete(pref);
if ( (val || old_value) && val !== old_value ) {
this._context[full_key] = val;
changed.add(full_key);
}
} else if ( val !== old_value ) {
this._context[full_key] = val;
changed.add(full_key);
}
}
}
updateContext(context) {
if ( ! context || typeof context !== 'object' )
return;
const changed = new Set;
this._updateContext(context, undefined, undefined, changed);
if ( changed.size )
this._rebuildContext();
}*/
setContext(context) {
this._context = context;
this._rebuildContext();
this._context_objects = new Set;
this._context = {};
this.updateContext(context);
}
@ -289,6 +411,7 @@ export default class SettingsContext extends EventEmitter {
get(key) {
if ( key.startsWith('context.') )
//return this.__context[key.slice(8)];
return getter(key.slice(8), this.__context);
if ( this.__cache.has(key) )

View file

@ -6,7 +6,7 @@
import {ColorAdjuster} from 'utilities/color';
import {setChildren} from 'utilities/dom';
import {has, make_enum, split_chars, shallow_object_equals} from 'utilities/object';
import {has, make_enum, split_chars, shallow_object_equals, set_equals} from 'utilities/object';
import {FFZEvent} from 'utilities/events';
import Module from 'utilities/module';
@ -1454,6 +1454,21 @@ export default class ChatHook extends Module {
if ( ! room )
return;
// We have to check that the available cheers haven't changed
// to avoid doing too many recalculations.
let new_bits = null;
if ( config && Array.isArray(config.orderedActions) ) {
new_bits = new Set;
for(const action of config.orderedActions)
if ( action && action.prefix )
new_bits.add(action.prefix);
}
if ( (! this._ffz_old_bits && ! new_bits) || set_equals(this._ffz_old_bits, new_bits) )
return;
this._ffz_old_bits = new_bits;
room.updateBitsConfig(formatBitsConfig(config));
this.updateChatLines();
}

View file

@ -318,14 +318,22 @@ export default class Scroller extends Module {
return;
const t = this;
this._doScroll = function() {
if ( ! t.ffz_freeze_enabled || ! t.state.ffzFrozen ) {
if ( t.ffz_smooth_scroll )
t.smoothScrollBottom();
else
t._old_scroll();
}
}
this._old_scroll = this.scrollToBottom;
this.scrollToBottom = function() {
if ( ! this.ffz_freeze_enabled || ! this.state.ffzFrozen ) {
if ( this.ffz_smooth_scroll )
this.smoothScrollBottom();
else
this._old_scroll();
}
if ( this._ffz_animation )
cancelAnimationFrame(this._ffz_animation);
this._ffz_animation = requestAnimationFrame(t._doScroll);
}
this._ffz_handleScroll = this.handleScrollEvent;

View file

@ -227,10 +227,13 @@ export const load = () => {
if ( loaded )
return;
loaded = true;
document.head.appendChild(createElement('link', {
href: FA_URL,
rel: 'stylesheet',
type: 'text/css',
crossOrigin: 'anonymouse'
crossOrigin: 'anonymous'
}));
}

View file

@ -116,6 +116,18 @@ export function shallow_object_equals(a, b) {
}
export function map_equals(a, b) {
if ( !(a instanceof Map) || !(b instanceof Map) || a.size !== b.size )
return false;
for(const [key, val] of a)
if ( ! b.has(key) || b.get(key) !== val )
return false;
return true;
}
export function set_equals(a,b) {
if ( !(a instanceof Set) || !(b instanceof Set) || a.size !== b.size )
return false;