1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-28 15:27:43 +00:00
* Added: Warning on the Profile Manager about profiles not matching when viewing the FFZ Control Center in a pop-out.
* API Added: Badges can now have custom CSS.
* API Added: Badges can now have a `click_handler()` method for running JS when clicked.
This commit is contained in:
SirStendec 2021-02-17 15:24:07 -05:00
parent 165e17c014
commit 3fb6d5957a
6 changed files with 183 additions and 10 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "frankerfacez", "name": "frankerfacez",
"author": "Dan Salvato LLC", "author": "Dan Salvato LLC",
"version": "4.20.66", "version": "4.20.67",
"description": "FrankerFaceZ is a Twitch enhancement suite.", "description": "FrankerFaceZ is a Twitch enhancement suite.",
"license": "Apache-2.0", "license": "Apache-2.0",
"scripts": { "scripts": {

View file

@ -162,7 +162,7 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, badge_ver
image, image,
image_set, image_set,
svg svg
})}`; })}${data.css || ''}`;
} }
@ -464,8 +464,11 @@ export default class Badges extends Module {
if ( ! container.dataset.roomId ) if ( ! container.dataset.roomId )
container = target.closest('[data-room-id]'); container = target.closest('[data-room-id]');
const room_id = container?.dataset?.roomId, const ds = container?.dataset,
room_login = container?.dataset?.room, room_id = ds?.roomId,
room_login = ds?.room,
user_id = ds?.userId,
user_login = ds?.user,
data = JSON.parse(target.dataset.badgeData); data = JSON.parse(target.dataset.badgeData);
if ( data == null ) if ( data == null )
@ -494,6 +497,11 @@ export default class Badges extends Module {
} else if ( p === 'ffz' ) { } else if ( p === 'ffz' ) {
const badge = this.badges[target.dataset.badge]; const badge = this.badges[target.dataset.badge];
if ( badge?.click_handler ) {
url = badge.click_handler(user_id, user_login, room_id, room_login, data, event);
break;
}
if ( badge?.click_url ) { if ( badge?.click_url ) {
url = badge.click_url; url = badge.click_url;
break; break;

View file

@ -1,5 +1,40 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--profile-manager tw-border-t tw-pd-y-1"> <div class="ffz--widget ffz--profile-manager tw-border-t tw-pd-y-1">
<section v-if="context.exclusive" class="tw-pd-b-2">
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
<h3 class="ffz-i-info">
{{ t('setting.context-difference', 'Your Profiles might not match.') }}
</h3>
{{ t('setting.context-difference.description',
'Since the Control Center is open in a new window, profiles may match differently here than on other Twitch windows.')
}}
<div v-if="context.can_proxy" class="tw-flex tw-align-items-center tw-mg-t-1">
<div class="ffz-checkbox tw-relative tw-tooltip__container">
<input
id="proxied"
ref="proxied"
type="checkbox"
class="ffz-checkbox__input"
:checked="context.proxied"
@change="onProxyCheck"
>
<label for="proxied" class="ffz-checkbox__label">
<span class="tw-mg-l-1">
{{ t('setting.context-difference.use', 'Use Original Windows\'s Context') }}
</span>
</label>
<div class="tw-tooltip ffz-balloon--md tw-tooltip--wrap tw-tooltip--down tw-tooltip--align-left">
{{ t('setting.context-difference.tip', 'Checking this will use the context from the original window, causing profiles and thier rules to match like they would in the window that opened this Control Center.') }}
</div>
</div>
</div>
</div>
</section>
<div class="tw-flex tw-align-items-center tw-pd-b-05"> <div class="tw-flex tw-align-items-center tw-pd-b-05">
<div class="tw-flex-grow-1"> <div class="tw-flex-grow-1">
{{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }} {{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }}
@ -237,6 +272,11 @@ export default {
}, },
methods: { methods: {
onProxyCheck() {
const val = this.$refs.proxied.checked;
this.context.setProxied(val);
},
edit(profile) { edit(profile) {
const item = { const item = {
full_key: 'data_management.profiles.edit_profile', full_key: 'data_management.profiles.edit_profile',

View file

@ -13,7 +13,7 @@ import Dialog from 'utilities/dialog';
import SettingMixin from './setting-mixin'; import SettingMixin from './setting-mixin';
import ProviderMixin from './provider-mixin'; import ProviderMixin from './provider-mixin';
import {parse_path} from 'src/settings'; import {NO_SYNC_KEYS, parse_path} from 'src/settings';
function format_term(term) { function format_term(term) {
return term.replace(/<[^>]*>/g, '').toLocaleLowerCase(); return term.replace(/<[^>]*>/g, '').toLocaleLowerCase();
@ -49,6 +49,8 @@ export default class MainMenu extends Module {
this.opened = false; this.opened = false;
this.showing = false; this.showing = false;
this.context = this.settings.context();
this.settings.addUI('profiles', { this.settings.addUI('profiles', {
path: 'Data Management @{"sort": 1000, "profile_warning": false} > Profiles @{"profile_warning": false}', path: 'Data Management @{"sort": 1000, "profile_warning": false} > Profiles @{"profile_warning": false}',
component: 'profile-manager' component: 'profile-manager'
@ -213,7 +215,64 @@ export default class MainMenu extends Module {
this.off('site.menu_button:clicked', this.dialog.toggleVisible, this.dialog); this.off('site.menu_button:clicked', this.dialog.toggleVisible, this.dialog);
} }
updateContext(context) {
if ( ! context )
context = this._context;
this._context = context;
if ( this.use_context ) {
if ( ! this.context._updateContext ) {
this.context._updateContext = this.context.updateContext;
this.context.updateContext = function(context) {
const accepted = {};
for(const key of NO_SYNC_KEYS)
if ( has(context, key) )
accepted[key] = context[key];
this._updateContext(accepted);
}
}
this.context._context = {};
this.context._updateContext({
...context,
can_proxy: context?.proxied || false
});
} else {
if ( this.context._updateContext ) {
this.context.updateContext = this.context._updateContext;
this.context._updateContext = null;
}
this.context.setContext({can_proxy: context?.proxied || false});
}
}
openExclusive() { openExclusive() {
window.addEventListener('message', event => {
const type = event.data?.ffz_type;
if ( type === 'context-update' )
this.updateContext({...event.data.ctx, proxied: true});
else if ( type === 'context-gone' ) {
this.log.info('Context proxy gone.');
this.updateContext({proxied: false});
}
});
try {
window.opener.postMessage({
ffz_type: 'request-context'
}, '*');
} catch(err) {
this.log.info('Unable to request settings context from opener.');
}
this.exclusive = true; this.exclusive = true;
this.dialog.exclusive = true; this.dialog.exclusive = true;
this.enable().then(() => this.dialog.show()); this.enable().then(() => this.dialog.show());
@ -594,7 +653,7 @@ export default class MainMenu extends Module {
const profiles = [], const profiles = [],
keys = {}; keys = {};
context = context || this.settings.main_context; context = context || this.context;
for(const profile of this.settings.__profiles) for(const profile of this.settings.__profiles)
profiles.push(keys[profile.id] = this.getProfileProxy(profile, context)); profiles.push(keys[profile.id] = this.getProfileProxy(profile, context));
@ -648,7 +707,7 @@ export default class MainMenu extends Module {
Vue = this.vue.Vue, Vue = this.vue.Vue,
settings = this.settings, settings = this.settings,
provider = settings.provider, provider = settings.provider,
context = settings.main_context, context = this.context,
[profiles, profile_keys] = this.getProfiles(); [profiles, profile_keys] = this.getProfiles();
let currentProfile = profile_keys[0]; let currentProfile = profile_keys[0];
@ -669,8 +728,16 @@ export default class MainMenu extends Module {
profile_keys, profile_keys,
currentProfile: profile_keys[0] || profiles[0], currentProfile: profile_keys[0] || profiles[0],
exclusive: this.exclusive,
can_proxy: context._context.can_proxy,
proxied: context._context.proxied,
has_update: this.has_update, has_update: this.has_update,
setProxied: val => {
this.use_context = val;
this.updateContext();
},
createProfile: data => { createProfile: data => {
const profile = settings.createProfile(data); const profile = settings.createProfile(data);
return t.getProfileProxy(profile, context); return t.getProfileProxy(profile, context);
@ -761,6 +828,9 @@ export default class MainMenu extends Module {
const profiles = context.manager.__profiles, const profiles = context.manager.__profiles,
ids = this.profiles = context.__profiles.map(profile => profile.id); ids = this.profiles = context.__profiles.map(profile => profile.id);
this.proxied = this.context.proxied;
this.can_proxy = this.context.can_proxy;
for(let i=0; i < profiles.length; i++) { for(let i=0; i < profiles.length; i++) {
const id = profiles[i].id, const id = profiles[i].id,
profile = profile_keys[id]; profile = profile_keys[id];

View file

@ -5,7 +5,7 @@
// ============================================================================ // ============================================================================
import {EventEmitter} from 'utilities/events'; import {EventEmitter} from 'utilities/events';
import {has, get as getter, array_equals, set_equals, map_equals} from 'utilities/object'; import {has, get as getter, array_equals, set_equals, map_equals, deep_equals} from 'utilities/object';
import * as DEFINITIONS from './types'; import * as DEFINITIONS from './types';
@ -217,8 +217,17 @@ export default class SettingsContext extends EventEmitter {
let changed = false; let changed = false;
for(const key in context) for(const key in context)
if ( has(context, key) && context[key] !== this._context[key] ) { if ( has(context, key) ) {
this._context[key] = context[key]; const val = context[key];
try {
if ( deep_equals(val, this._context[key]) )
continue;
} catch(err) {
/* no-op */
// This can catch a recursive structure error.
}
this._context[key] = val;
changed = true; changed = true;
} }

View file

@ -16,6 +16,18 @@ import * as FILTERS from './filters';
import * as CLEARABLES from './clearables'; import * as CLEARABLES from './clearables';
function postMessage(target, msg) {
try {
target.postMessage(msg, '*');
return true;
} catch(err) {
return false;
}
}
export const NO_SYNC_KEYS = ['session'];
// ============================================================================ // ============================================================================
// SettingsManager // SettingsManager
// ============================================================================ // ============================================================================
@ -107,12 +119,46 @@ export default class SettingsManager extends Module {
this.emit(`:uses_changed:${key}`, new_uses, old_uses); this.emit(`:uses_changed:${key}`, new_uses, old_uses);
}); });
this.main_context.on('context_changed', () => this._updateContextProxies());
this._context_proxies = new Set;
window.addEventListener('message', event => {
const type = event.data?.ffz_type;
if ( type === 'request-context' ) {
this._context_proxies.add(event.source);
this._updateContextProxies(event.source);
}
});
window.addEventListener('beforeunload', () => {
for(const proxy of this._context_proxies)
postMessage(proxy, {ffz_type: 'context-gone'});
});
// Don't wait around to be required. // Don't wait around to be required.
this._start_time = performance.now(); this._start_time = performance.now();
this.enable(); this.enable();
} }
_updateContextProxies(proxy) {
if ( ! proxy && ! this._context_proxies.size )
return;
const ctx = JSON.parse(JSON.stringify(this.main_context._context));
for(const key of NO_SYNC_KEYS)
if ( has(ctx, key) )
delete ctx[key];
if ( proxy )
postMessage(proxy, {ffz_type: 'context-update', ctx});
else
for(const proxy of this._context_proxies)
postMessage(proxy, {ffz_type: 'context-update', ctx});
}
addFilter(key, data) { addFilter(key, data) {
if ( this.filters[key] ) if ( this.filters[key] )