diff --git a/package.json b/package.json index 36171324..d41c58da 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.71.0", + "version": "4.72.0", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", diff --git a/src/modules/chat/actions/actions.jsx b/src/modules/chat/actions/actions.jsx index 54d07018..4e0012ee 100644 --- a/src/modules/chat/actions/actions.jsx +++ b/src/modules/chat/actions/actions.jsx @@ -27,6 +27,11 @@ export default class Actions extends Module { this.actions = {}; this.renderers = {}; + this.filterAction = (x) => x.appearance && + this.renderers[x.appearance.type] && + (! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) && + (! x.action || this.actions[x.action]); + this.settings.add('chat.actions.size', { default: 16, ui: { @@ -82,11 +87,7 @@ export default class Actions extends Module { this.settings.add('chat.actions.hover', { process: (ctx, val) => - val.filter(x => x.appearance && - this.renderers[x.appearance.type] && - (! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) && - (! x.action || this.actions[x.action]) - ), + val.filter(this.filterAction), default: [ {v: {action: 'pin', appearance: {type: 'icon', icon: 'ffz-i-pin'}, options: {}, display: {mod_icons: true}}}, @@ -119,11 +120,7 @@ export default class Actions extends Module { this.settings.add('chat.actions.inline', { // Filter out actions process: (ctx, val) => - val.filter(x => x.appearance && - this.renderers[x.appearance.type] && - (! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) && - (! x.action || this.actions[x.action]) - ), + val.filter(this.filterAction), default: [ {v: {action: 'ban', appearance: {type: 'icon', icon: 'ffz-i-block'}, options: {}, display: {mod: true, mod_icons: true, deleted: false}}}, @@ -157,11 +154,7 @@ export default class Actions extends Module { this.settings.add('chat.actions.user-context', { // Filter out actions process: (ctx, val) => - val.filter(x => x.type || (x.appearance && - this.renderers[x.appearance.type] && - (! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) && - (! x.action || this.actions[x.action]) - )), + val.filter(x => x.type || this.filterAction(x)), default: [], type: 'array_merge', @@ -187,11 +180,7 @@ export default class Actions extends Module { this.settings.add('chat.actions.room', { // Filter out actions process: (ctx, val) => - val.filter(x => x.type || (x.appearance && - this.renderers[x.appearance.type] && - (! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) && - (! x.action || this.actions[x.action]) - )), + val.filter(x => x.type || this.filterAction(x)), default: [], type: 'array_merge', @@ -316,20 +305,24 @@ export default class Actions extends Module { } if ( is_dev ) { - overrides.removeAction = key => { - const existing = this.actions[key]; - if ( existing && existing.__source !== addon_id ) - module.log.warn('[DEV-CHECK] Removed un-owned action with actions.removeAction:', key, ' owner:', existing.__source ?? 'ffz'); + overrides.removeAction = (...key) => { + for(const entry of key) { + const existing = this.actions[entry]; + if ( existing && existing.__source !== addon_id ) + module.log.warn('[DEV-CHECK] Removed un-owned action with actions.removeAction:', entry, ' owner:', existing.__source ?? 'ffz'); + } - return this.removeAction(key); + return this.removeAction(...key); }; - overrides.removeRenderer = key => { - const existing = this.renderers[key]; - if ( existing && existing.__source !== addon_id ) - module.log.warn('[DEV-CHECK] Removed un-owned renderer with actions.removeRenderer:', key, ' owner:', existing.__source ?? 'ffz'); + overrides.removeRenderer = (...key) => { + for(const entry of key) { + const existing = this.renderers[entry]; + if ( existing && existing.__source !== addon_id ) + module.log.warn('[DEV-CHECK] Removed un-owned renderer with actions.removeRenderer:', entry, ' owner:', existing.__source ?? 'ffz'); + } - return this.removeRenderer(key); + return this.removeRenderer(...key); } warnings.actions = 'Please use addAction() or removeAction()'; @@ -367,22 +360,49 @@ export default class Actions extends Module { this._updateContexts(); } + getActions() { + return {...this.actions} + } - removeAction(key) { - if ( ! has(this.actions, key) ) - return; + getAction(key) { + return this.actions[key] ?? null; + } - delete this.actions[key]; - this._updateContexts(); + getRenderer(key) { + return this.renderers[key] ?? null; + } + + getRenderers() { + return {...this.renderers} + } + + removeAction(...keys) { + let changed = false; + for(const entry of keys) { + if ( ! has(this.actions, entry) ) + continue; + + delete this.actions[entry]; + changed = true; + } + + if ( changed ) + this._updateContexts(); } - removeRenderer(key) { - if ( ! has(this.renderers, key) ) - return; + removeRenderer(...keys) { + let changed = false; + for(const entry of keys) { + if ( ! has(this.renderers, entry) ) + return; - delete this.renderers[key]; - this._updateContexts(); + delete this.renderers[entry]; + changed = true; + } + + if ( changed ) + this._updateContexts(); } diff --git a/src/modules/chat/badges.jsx b/src/modules/chat/badges.jsx index 551cf1a4..de888f75 100644 --- a/src/modules/chat/badges.jsx +++ b/src/modules/chat/badges.jsx @@ -1223,6 +1223,11 @@ export default class Badges extends Module { } + getBadge(badge_id) { + return this.badges[badge_id] ?? null; + } + + removeBadge(badge_id, generate_css = true) { if ( ! this.badges[badge_id] ) return; diff --git a/src/modules/chat/user.ts b/src/modules/chat/user.ts index 3d42d2cb..50cb962e 100644 --- a/src/modules/chat/user.ts +++ b/src/modules/chat/user.ts @@ -154,6 +154,13 @@ export default class User { } + getBadges() { + if ( this.badges ) + return [...this.badges._cache]; + return []; + } + + getBadge(badge_id: string) { if ( this.badges ) for(const badge of this.badges._cache) diff --git a/src/modules/emote_card/components/card.vue b/src/modules/emote_card/components/card.vue index 433b0751..0d28a2fd 100644 --- a/src/modules/emote_card/components/card.vue +++ b/src/modules/emote_card/components/card.vue @@ -370,8 +370,8 @@ export default { if ( ! parent ) parent = document.body; - const box = el.getBoundingClientRect(), - pbox = parent.getBoundingClientRect(); + const box = el.getBoundingClientRect(); + let pbox = parent.getBoundingClientRect(); if ( box.top < pbox.top ) { el.style.top = `${el.offsetTop + (pbox.top - box.top)}px`; @@ -447,4 +447,4 @@ export default { } - \ No newline at end of file + diff --git a/src/modules/main_menu/components/action-editor.vue b/src/modules/main_menu/components/action-editor.vue index 27cfd4ab..6d85a8e9 100644 --- a/src/modules/main_menu/components/action-editor.vue +++ b/src/modules/main_menu/components/action-editor.vue @@ -79,6 +79,12 @@ v-if="renderer" v-model="edit_data.appearance" /> + + @@ -128,6 +134,28 @@ + + + {{ t('setting.actions.edit-visible.following', 'Following User') }} + + + + + {{ t('setting.unset', 'Unset') }} + + + {{ t('setting.true', 'True') }} + + + {{ t('setting.false', 'False') }} + + + + {{ t('setting.actions.edit-visible.deleted', 'Message Deleted') }} @@ -459,7 +487,7 @@ import {has, maybe_call, deep_copy} from 'utilities/object'; let id = 0; export default { - props: ['vuectx', 'action', 'data', 'inline', 'mod_icons', 'context', 'modifiers', 'hover_modifier'], + props: ['vuectx', 'action', 'data', 'inline', 'mod_icons', 'extra_appearance', 'context', 'modifiers', 'hover_modifier'], data() { return { @@ -487,6 +515,10 @@ export default { return this.context && this.context.includes('message') }, + has_following() { + return this.context && this.context.includes('following') + }, + has_mode() { return this.context && this.context.includes('room-mode') }, @@ -642,6 +674,12 @@ export default { if ( disp.disable ) return this.t('setting.actions.visible.never', 'never'); + if ( disp.following === true ) + out.push(this.t('setting.actions.visible.following', 'when following user')); + + else if ( disp.following === false ) + out.push(this.t('setting.actions.visible.unfollowing', 'when not following user')); + if ( disp.mod === true ) out.push(this.t('setting.actions.visible.mod', 'when moderator')); @@ -824,4 +862,4 @@ export default { } - \ No newline at end of file + diff --git a/src/modules/main_menu/components/chat-actions.vue b/src/modules/main_menu/components/chat-actions.vue index 56c48475..a6b2a008 100644 --- a/src/modules/main_menu/components/chat-actions.vue +++ b/src/modules/main_menu/components/chat-actions.vue @@ -376,6 +376,7 @@ :inline="item.inline" :mod_icons="has_icons" :context="item.context" + :extra_appearance="item.extra_appearance_editor" :vuectx="context" :modifiers="item.modifiers" :hover_modifier="item.hover_modifier" @@ -817,4 +818,4 @@ export default { } } - \ No newline at end of file + diff --git a/src/sites/twitch-twilight/modules/css_tweaks/styles/swap-sidebars.scss b/src/sites/twitch-twilight/modules/css_tweaks/styles/swap-sidebars.scss index e9ccd7a5..1e6057f4 100644 --- a/src/sites/twitch-twilight/modules/css_tweaks/styles/swap-sidebars.scss +++ b/src/sites/twitch-twilight/modules/css_tweaks/styles/swap-sidebars.scss @@ -10,6 +10,11 @@ } } +.right-column--theatre { + /* 1 higher than default Twitch, to make the expand button appear properly */ + z-index: 3001; +} + .channel-root { width: unset !important; } @@ -98,4 +103,4 @@ body .whispers--theatre-mode.whispers--right-column-expanded-beside { left: calc(var(--ffz-chat-width) + 5rem) !important; right: 40rem !important; } -} \ No newline at end of file +} diff --git a/src/sites/twitch-twilight/styles/fixes.scss b/src/sites/twitch-twilight/styles/fixes.scss index 5c083751..edeb2f9c 100644 --- a/src/sites/twitch-twilight/styles/fixes.scss +++ b/src/sites/twitch-twilight/styles/fixes.scss @@ -11,6 +11,7 @@ position: absolute !important; } -#root.ffz-has-dialog { +#root { min-height: 100%; -} \ No newline at end of file + min-width: 100%; +} diff --git a/src/sites/twitch-twilight/styles/main.scss b/src/sites/twitch-twilight/styles/main.scss index bdefc036..530e813b 100644 --- a/src/sites/twitch-twilight/styles/main.scss +++ b/src/sites/twitch-twilight/styles/main.scss @@ -12,4 +12,4 @@ //@import 'host_options'; @import 'featured_follow'; @import 'mod_card'; -@import 'easteregg'; \ No newline at end of file +@import 'easteregg'; diff --git a/src/utilities/compat/fine.ts b/src/utilities/compat/fine.ts index 185a4c48..4faa20f3 100644 --- a/src/utilities/compat/fine.ts +++ b/src/utilities/compat/fine.ts @@ -270,6 +270,47 @@ export default class Fine extends Module<'site.fine', FineEvents> { return null; } + searchParentNode( + input: InputNode, + criteria: NodeCriteria, + max_depth = 15, + depth = 0, + traverse_roots = true + ): TNode | null { + if ( depth > max_depth ) + return null; + + const node = this.resolveNode(input); + + // If we don't have a node, then stop. + if ( ! node ) + return null; + + if ( criteria(node) ) + return node as TNode; + + if ( node.return ) { + const result = this.searchParentNode(node.return, criteria, max_depth, depth+1, traverse_roots); + if ( result ) + return result as TNode; + } + + // Stupid code for traversing up into another React root. + const inst = node.stateNode; + if ( traverse_roots && (inst as any)?.containerInfo ) { + const parent = (inst as any).containerInfo?.parentElement as Node | undefined, + parent_node = parent && this.getReactInstance(parent); + + if ( parent_node ) { + const result = this.searchParentNode(parent_node, criteria, max_depth, depth+1, traverse_roots); + if ( result ) + return result as TNode; + } + } + + return null; + } + searchNode( input: InputNode, criteria: NodeCriteria, @@ -286,7 +327,7 @@ export default class Fine extends Module<'site.fine', FineEvents> { if ( ! node ) return null; - if ( node && criteria(node) ) + if ( criteria(node) ) return node as TNode; // If the node has disabled scanning, don't scan into its children.