mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.20.67
* 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:
parent
165e17c014
commit
3fb6d5957a
6 changed files with 183 additions and 10 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.20.66",
|
||||
"version": "4.20.67",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
|
|
@ -162,7 +162,7 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, badge_ver
|
|||
image,
|
||||
image_set,
|
||||
svg
|
||||
})}`;
|
||||
})}${data.css || ''}`;
|
||||
}
|
||||
|
||||
|
||||
|
@ -464,8 +464,11 @@ export default class Badges extends Module {
|
|||
if ( ! container.dataset.roomId )
|
||||
container = target.closest('[data-room-id]');
|
||||
|
||||
const room_id = container?.dataset?.roomId,
|
||||
room_login = container?.dataset?.room,
|
||||
const ds = container?.dataset,
|
||||
room_id = ds?.roomId,
|
||||
room_login = ds?.room,
|
||||
user_id = ds?.userId,
|
||||
user_login = ds?.user,
|
||||
data = JSON.parse(target.dataset.badgeData);
|
||||
|
||||
if ( data == null )
|
||||
|
@ -494,6 +497,11 @@ export default class Badges extends Module {
|
|||
|
||||
} else if ( p === 'ffz' ) {
|
||||
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 ) {
|
||||
url = badge.click_url;
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,40 @@
|
|||
<template lang="html">
|
||||
<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-grow-1">
|
||||
{{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }}
|
||||
|
@ -237,6 +272,11 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
onProxyCheck() {
|
||||
const val = this.$refs.proxied.checked;
|
||||
this.context.setProxied(val);
|
||||
},
|
||||
|
||||
edit(profile) {
|
||||
const item = {
|
||||
full_key: 'data_management.profiles.edit_profile',
|
||||
|
|
|
@ -13,7 +13,7 @@ import Dialog from 'utilities/dialog';
|
|||
import SettingMixin from './setting-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) {
|
||||
return term.replace(/<[^>]*>/g, '').toLocaleLowerCase();
|
||||
|
@ -49,6 +49,8 @@ export default class MainMenu extends Module {
|
|||
this.opened = false;
|
||||
this.showing = false;
|
||||
|
||||
this.context = this.settings.context();
|
||||
|
||||
this.settings.addUI('profiles', {
|
||||
path: 'Data Management @{"sort": 1000, "profile_warning": false} > Profiles @{"profile_warning": false}',
|
||||
component: 'profile-manager'
|
||||
|
@ -213,7 +215,64 @@ export default class MainMenu extends Module {
|
|||
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() {
|
||||
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.dialog.exclusive = true;
|
||||
this.enable().then(() => this.dialog.show());
|
||||
|
@ -594,7 +653,7 @@ export default class MainMenu extends Module {
|
|||
const profiles = [],
|
||||
keys = {};
|
||||
|
||||
context = context || this.settings.main_context;
|
||||
context = context || this.context;
|
||||
|
||||
for(const profile of this.settings.__profiles)
|
||||
profiles.push(keys[profile.id] = this.getProfileProxy(profile, context));
|
||||
|
@ -648,7 +707,7 @@ export default class MainMenu extends Module {
|
|||
Vue = this.vue.Vue,
|
||||
settings = this.settings,
|
||||
provider = settings.provider,
|
||||
context = settings.main_context,
|
||||
context = this.context,
|
||||
[profiles, profile_keys] = this.getProfiles();
|
||||
|
||||
let currentProfile = profile_keys[0];
|
||||
|
@ -669,8 +728,16 @@ export default class MainMenu extends Module {
|
|||
profile_keys,
|
||||
currentProfile: profile_keys[0] || profiles[0],
|
||||
|
||||
exclusive: this.exclusive,
|
||||
can_proxy: context._context.can_proxy,
|
||||
proxied: context._context.proxied,
|
||||
has_update: this.has_update,
|
||||
|
||||
setProxied: val => {
|
||||
this.use_context = val;
|
||||
this.updateContext();
|
||||
},
|
||||
|
||||
createProfile: data => {
|
||||
const profile = settings.createProfile(data);
|
||||
return t.getProfileProxy(profile, context);
|
||||
|
@ -761,6 +828,9 @@ export default class MainMenu extends Module {
|
|||
const profiles = context.manager.__profiles,
|
||||
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++) {
|
||||
const id = profiles[i].id,
|
||||
profile = profile_keys[id];
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// ============================================================================
|
||||
|
||||
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';
|
||||
|
||||
|
@ -217,8 +217,17 @@ export default class SettingsContext extends EventEmitter {
|
|||
let changed = false;
|
||||
|
||||
for(const key in context)
|
||||
if ( has(context, key) && context[key] !== this._context[key] ) {
|
||||
this._context[key] = context[key];
|
||||
if ( has(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,18 @@ import * as FILTERS from './filters';
|
|||
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
|
||||
// ============================================================================
|
||||
|
@ -107,12 +119,46 @@ export default class SettingsManager extends Module {
|
|||
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.
|
||||
this._start_time = performance.now();
|
||||
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) {
|
||||
if ( this.filters[key] )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue