diff --git a/src/main.js b/src/main.js index 1e2a243a..522164cc 100644 --- a/src/main.js +++ b/src/main.js @@ -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: () => diff --git a/src/modules/chat/index.js b/src/modules/chat/index.js index c578d9ac..c6dd28db 100644 --- a/src/modules/chat/index.js +++ b/src/modules/chat/index.js @@ -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 ) diff --git a/src/settings/context.js b/src/settings/context.js index 84b8bbc5..cb6c5360 100644 --- a/src/settings/context.js +++ b/src/settings/context.js @@ -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) ) diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index bfe53ef8..d4a1093d 100644 --- a/src/sites/twitch-twilight/modules/chat/index.js +++ b/src/sites/twitch-twilight/modules/chat/index.js @@ -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(); } diff --git a/src/sites/twitch-twilight/modules/chat/scroller.js b/src/sites/twitch-twilight/modules/chat/scroller.js index 6c9e89bb..5cd9bde0 100644 --- a/src/sites/twitch-twilight/modules/chat/scroller.js +++ b/src/sites/twitch-twilight/modules/chat/scroller.js @@ -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; diff --git a/src/utilities/font-awesome.js b/src/utilities/font-awesome.js index 8aa0b9ee..79bcab66 100644 --- a/src/utilities/font-awesome.js +++ b/src/utilities/font-awesome.js @@ -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' })); -} \ No newline at end of file +} + diff --git a/src/utilities/object.js b/src/utilities/object.js index 220181e9..aeafd272 100644 --- a/src/utilities/object.js +++ b/src/utilities/object.js @@ -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;