mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-08 07:10:54 +00:00
4.16.0
* Added: Show the FFZ menu button on new dashboard pages. * Fixed: Synchronize settings with the `dashboard.twitch.tv` subdomain. * Fixed: Performance issue with metadata tool-tips being calculated too frequently. * Fixed: Metadata not appearing in theater mode when portrait mode is enabled. * API Added: Chat Action types can now override rendering.
This commit is contained in:
parent
347919c51a
commit
ff0f0ea074
14 changed files with 317 additions and 42 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.15.5",
|
||||
"version": "4.16.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
|
|
@ -82,7 +82,9 @@ class FFZBridge extends Module {
|
|||
ffz_type: 'loaded',
|
||||
data: out
|
||||
});
|
||||
}
|
||||
|
||||
} else if ( msg.ffz_type === 'change' )
|
||||
this.onChange(msg);
|
||||
}
|
||||
|
||||
send(msg) { // eslint-disable-line class-methods-use-this
|
||||
|
@ -91,6 +93,17 @@ class FFZBridge extends Module {
|
|||
} catch(err) { this.log.error('send error', err); /* no-op */ }
|
||||
}
|
||||
|
||||
onChange(msg) {
|
||||
const key = msg.key,
|
||||
value = msg.value,
|
||||
deleted = msg.deleted;
|
||||
|
||||
if ( deleted )
|
||||
this.settings.provider.delete(key);
|
||||
else
|
||||
this.settings.provider.set(key, value);
|
||||
}
|
||||
|
||||
onProviderChange(key, value, deleted) {
|
||||
this.send({
|
||||
ffz_type: 'change',
|
||||
|
|
|
@ -437,12 +437,11 @@ export default class Actions extends Module {
|
|||
if ( ! data || ! data.action || ! data.appearance )
|
||||
continue;
|
||||
|
||||
const ap = data.appearance || {},
|
||||
disp = data.display || {},
|
||||
let ap = data.appearance || {};
|
||||
const disp = data.display || {},
|
||||
act = this.actions[data.action];
|
||||
|
||||
def = this.renderers[ap.type];
|
||||
|
||||
if ( ! def || disp.disabled ||
|
||||
if ( ! act || disp.disabled ||
|
||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||
(disp.mod != null && disp.mod !== (current_user ? !!current_user.mod : false)) ||
|
||||
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : false)) ||
|
||||
|
@ -453,12 +452,23 @@ export default class Actions extends Module {
|
|||
(disp.followersOnly != null && disp.followersOnly !== current_room.followersOnly) )
|
||||
continue;
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, null, current_room, current_user, mod_icons);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
const def = this.renderers[ap.type];
|
||||
if ( ! def )
|
||||
continue;
|
||||
|
||||
const has_color = def.colored && ap.color,
|
||||
disabled = maybe_call(act.disabled, this, data, null, current_room, current_user, mod_icons) || false,
|
||||
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||
contents = def.render.call(this, ap, createElement, color);
|
||||
|
||||
actions.push(<button
|
||||
class={`ffz-tooltip tw-pd-x-05 ffz-mod-icon mod-icon tw-c-text-alt-2${has_color ? ' colored' : ''}`}
|
||||
class={`ffz-tooltip tw-pd-x-05 ffz-mod-icon mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -552,19 +562,29 @@ export default class Actions extends Module {
|
|||
} else if ( ! data.action || ! data.appearance )
|
||||
continue;
|
||||
|
||||
const ap = data.appearance || {},
|
||||
disp = data.display || {},
|
||||
let ap = data.appearance || {};
|
||||
const disp = data.display || {},
|
||||
act = this.actions[data.action];
|
||||
|
||||
def = this.renderers[ap.type];
|
||||
|
||||
if ( ! def || disp.disabled ||
|
||||
if ( ! act || disp.disabled ||
|
||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
||||
(disp.staff != null && disp.staff !== (u ? !!u.staff : false)) ||
|
||||
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
||||
continue;
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, r, u, mod_icons);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
const def = this.renderers[ap.type];
|
||||
if ( ! def )
|
||||
continue;
|
||||
|
||||
const has_color = def.colored && ap.color,
|
||||
disabled = maybe_call(act.disabled, this, data, msg, r, u, mod_icons) || false,
|
||||
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||
contents = def.render.call(this, ap, createElement, color);
|
||||
|
||||
|
@ -572,7 +592,8 @@ export default class Actions extends Module {
|
|||
lines.push(line = []);
|
||||
|
||||
const btn = (<button
|
||||
class="ffz-tooltip ffz-tooltip--no-mouse tw-button tw-button--text"
|
||||
class={`ffz-tooltip ffz-tooltip--no-mouse tw-button tw-button--text${disabled ? ' tw-button--disabled disabled' : ''}`}
|
||||
disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -631,20 +652,30 @@ export default class Actions extends Module {
|
|||
if ( ! data.action || ! data.appearance )
|
||||
continue;
|
||||
|
||||
const ap = data.appearance || {},
|
||||
disp = data.display || {},
|
||||
let ap = data.appearance || {};
|
||||
const disp = data.display || {},
|
||||
keys = disp.keys,
|
||||
act = this.actions[data.action];
|
||||
|
||||
def = this.renderers[ap.type];
|
||||
|
||||
if ( ! def || disp.disabled ||
|
||||
if ( ! act || disp.disabled ||
|
||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
||||
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : false)) ||
|
||||
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
||||
continue;
|
||||
|
||||
if ( act.override_appearance ) {
|
||||
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons);
|
||||
if ( out )
|
||||
ap = out;
|
||||
}
|
||||
|
||||
const def = this.renderers[ap.type];
|
||||
if ( ! def )
|
||||
continue;
|
||||
|
||||
const has_color = def.colored && ap.color,
|
||||
disabled = maybe_call(act.disabled, this, data, msg, current_room, current_user, mod_icons) || false,
|
||||
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||
contents = def.render.call(this, ap, createElement, color);
|
||||
|
||||
|
@ -655,7 +686,8 @@ export default class Actions extends Module {
|
|||
|
||||
had_action = true;
|
||||
list.push(<button
|
||||
class={`ffz-tooltip ffz-mod-icon mod-icon tw-c-text-alt-2${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}`}
|
||||
class={`ffz-tooltip ffz-mod-icon mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}`}
|
||||
disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -799,6 +831,9 @@ export default class Actions extends Module {
|
|||
if ( ! data )
|
||||
return;
|
||||
|
||||
if ( target.classList.contains('disabled') )
|
||||
return;
|
||||
|
||||
if ( ! data.definition.click ) {
|
||||
if ( data.definition.context )
|
||||
return this.handleContext(event);
|
||||
|
@ -823,6 +858,9 @@ export default class Actions extends Module {
|
|||
if ( ! data )
|
||||
return;
|
||||
|
||||
if ( target.classList.contains('disabled') )
|
||||
return;
|
||||
|
||||
if ( target._ffz_tooltip$0 )
|
||||
target._ffz_tooltip$0.hide();
|
||||
|
||||
|
|
|
@ -111,6 +111,8 @@ export default class Metadata extends Module {
|
|||
subtitle: () => this.i18n.t('metadata.uptime.subtitle', 'Uptime'),
|
||||
|
||||
tooltip(data) {
|
||||
console.log('tool-tip');
|
||||
|
||||
if ( ! data.created )
|
||||
return null;
|
||||
|
||||
|
@ -444,8 +446,7 @@ export default class Metadata extends Module {
|
|||
if ( ! label )
|
||||
return destroy();
|
||||
|
||||
const tooltip = maybe_call(def.tooltip, this, data),
|
||||
order = maybe_call(def.order, this, data),
|
||||
const order = maybe_call(def.order, this, data),
|
||||
color = maybe_call(def.color, this, data) || '';
|
||||
|
||||
if ( ! el ) {
|
||||
|
@ -468,7 +469,7 @@ export default class Metadata extends Module {
|
|||
el = (<div
|
||||
class={`tw-align-items-center tw-inline-flex tw-relative tw-tooltip-wrapper ffz-stat tw-stat ffz-stat--fix-padding ${border ? 'tw-mg-l-1' : 'tw-mg-l-05 ffz-mg-r--05'}`}
|
||||
data-key={key}
|
||||
tip_content={tooltip}
|
||||
tip_content={null}
|
||||
>
|
||||
{btn = (<button
|
||||
class={`tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-top-left-radius-medium tw-core-button tw-core-button--padded tw-core-button--text ${inherit ? 'ffz-c-text-inherit' : 'tw-c-text-base'} tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ${border ? 'tw-border-l tw-border-t tw-border-b' : 'tw-font-size-5 tw-regular'}`}
|
||||
|
@ -493,7 +494,7 @@ export default class Metadata extends Module {
|
|||
btn = popup = el = (<button
|
||||
class={`ffz-stat tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-top-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-right-radius-medium tw-core-button tw-core-button--text ${inherit ? 'ffz-c-text-inherit' : 'tw-c-text-base'} tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative tw-pd-x-05 ffz-stat--fix-padding ${border ? 'tw-border tw-mg-l-1' : 'tw-font-size-5 tw-regular tw-mg-l-05 ffz-mg-r--05'}`}
|
||||
data-key={key}
|
||||
tip_content={tooltip}
|
||||
tip_content={null}
|
||||
>
|
||||
<div class="tw-align-items-center tw-flex tw-flex-grow-0 tw-justify-center">
|
||||
{icon}
|
||||
|
@ -592,7 +593,7 @@ export default class Metadata extends Module {
|
|||
el = (<div
|
||||
class="tw-align-items-center tw-inline-flex tw-relative tw-tooltip-wrapper ffz-stat tw-stat tw-mg-l-1"
|
||||
data-key={key}
|
||||
tip_content={tooltip}
|
||||
tip_content={null}
|
||||
>
|
||||
{icon}
|
||||
{stat = <span class={`${icon ? 'tw-mg-l-05 ' : ''}ffz-stat-text tw-stat__value`} />}
|
||||
|
@ -627,9 +628,12 @@ export default class Metadata extends Module {
|
|||
logger: this.log,
|
||||
live: false,
|
||||
html: true,
|
||||
content: () => el.tip_content,
|
||||
content: () => maybe_call(def.tooltip, this, el._ffz_data),
|
||||
onShow: (t, tip) => el.tip = tip,
|
||||
onHide: () => el.tip = null,
|
||||
onHide: () => {
|
||||
el.tip = null;
|
||||
el.tip_content = null;
|
||||
},
|
||||
popper: {
|
||||
placement: 'bottom',
|
||||
modifiers: {
|
||||
|
@ -655,10 +659,12 @@ export default class Metadata extends Module {
|
|||
if ( el._ffz_order !== order )
|
||||
el.style.order = el._ffz_order = order;
|
||||
|
||||
if ( el.tip_content !== tooltip ) {
|
||||
el.tip_content = tooltip;
|
||||
if ( el.tip )
|
||||
if ( el.tip ) {
|
||||
const tooltip = maybe_call(def.tooltip, this, data);
|
||||
if ( el.tip_content !== tooltip ) {
|
||||
el.tip_content = tooltip;
|
||||
setChildren(el.tip.element, tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -180,12 +180,14 @@ export class LocalStorageProvider extends SettingsProvider {
|
|||
this._cached.set(key, value);
|
||||
localStorage.setItem(this.prefix + key, JSON.stringify(value));
|
||||
this.broadcast({type: 'set', key});
|
||||
this.emit('set', key, value, false);
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
this._cached.delete(key);
|
||||
localStorage.removeItem(this.prefix + key);
|
||||
this.broadcast({type: 'delete', key});
|
||||
this.emit('set', key, undefined, true);
|
||||
}
|
||||
|
||||
has(key) {
|
||||
|
|
|
@ -42,8 +42,11 @@ export default class Twilight extends BaseSite {
|
|||
this.populateModules();
|
||||
|
||||
this.web_munch.known(Twilight.KNOWN_MODULES);
|
||||
|
||||
this.router.route(Twilight.ROUTES);
|
||||
this.router.routeName(Twilight.ROUTE_NAMES);
|
||||
|
||||
this.router.route(Twilight.DASH_ROUTES, 'dashboard.twitch.tv');
|
||||
}
|
||||
|
||||
onEnable() {
|
||||
|
@ -205,7 +208,8 @@ Twilight.CHAT_ROUTES = [
|
|||
'dash',
|
||||
'embed-chat',
|
||||
'squad',
|
||||
'command-center'
|
||||
'command-center',
|
||||
'dash-stream-manager'
|
||||
];
|
||||
|
||||
|
||||
|
@ -220,6 +224,47 @@ Twilight.ROUTE_NAMES = {
|
|||
};
|
||||
|
||||
|
||||
Twilight.SUNLIGHT_ROUTES = [
|
||||
'dash-stream-manager',
|
||||
'dash-channel-analytics',
|
||||
'dash-stream-summary',
|
||||
'dash-achievements',
|
||||
'dash-roles',
|
||||
'dash-activity',
|
||||
'dash-channel-points',
|
||||
'dash-video-producer',
|
||||
'dash-edit-video',
|
||||
'dash-collections',
|
||||
'dash-edit-collection',
|
||||
'dash-clips',
|
||||
'dash-settings-moderation',
|
||||
'dash-settings-channel',
|
||||
'dash-settings-revenue',
|
||||
'dash-extensions',
|
||||
'dash-streaming-tools'
|
||||
];
|
||||
|
||||
|
||||
Twilight.DASH_ROUTES = {
|
||||
'dash-stream-manager': '/u/:userName/stream-manager',
|
||||
'dash-channel-analytics': '/u/:userName/channel-analytics',
|
||||
'dash-stream-summary': '/u/:userName/stream-summary',
|
||||
'dash-achievements': '/u/:userName/achievements',
|
||||
'dash-roles': '/u/:userName/community/roles',
|
||||
'dash-activity': '/u/:userName/community/activity',
|
||||
'dash-channel-points': '/u/:userName/community/channel-points',
|
||||
'dash-video-producer': '/u/:userName/content/video-producer',
|
||||
'dash-edit-video': '/u/:userName/content/video-producer/edit/:videoID',
|
||||
'dash-collections': '/u/:userName/content/collections',
|
||||
'dash-edit-collection': '/u/:userName/content/collections/:collectionID',
|
||||
'dash-clips': '/u/:userName/content/clips',
|
||||
'dash-settings-moderation': '/u/:userName/settings/moderation',
|
||||
'dash-settings-channel': '/u/:userName/settings/channel',
|
||||
'dash-settings-revenue': '/u/:userName/settings/revenue',
|
||||
'dash-extensions': '/u/:userName/extensions',
|
||||
'dash-streaming-tools': '/u/:userName/broadcast',
|
||||
};
|
||||
|
||||
Twilight.ROUTES = {
|
||||
'front-page': '/',
|
||||
'collection': '/collections/:collectionID',
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.channel-root__scroll-area--theatre-mode .channel-info-bar {
|
||||
bottom: calc(10rem + var(--ffz-chat-height)) !important;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.channel-root__scroll-area--theatre-mode .channel-info-bar {
|
||||
right: 40rem !important;
|
||||
}
|
|
@ -101,6 +101,22 @@ export default class Layout extends Module {
|
|||
changed: val => this.css_tweaks.toggle('portrait-swapped', val)
|
||||
});
|
||||
|
||||
this.settings.add('layout.use-portrait-meta', {
|
||||
requires: ['layout.inject-portrait', 'player.theatre.metadata'],
|
||||
process(ctx) {
|
||||
return ctx.get('layout.inject-portrait') && ctx.get('player.theatre.metadata')
|
||||
},
|
||||
changed: val => this.css_tweaks.toggle('portrait-metadata', val)
|
||||
});
|
||||
|
||||
this.settings.add('layout.use-portrait-meta-top', {
|
||||
requires: ['layout.use-portrait-meta', 'layout.portrait-invert'],
|
||||
process(ctx) {
|
||||
return ctx.get('layout.use-portrait-meta') && ! ctx.get('layout.portrait-invert')
|
||||
},
|
||||
changed: val => this.css_tweaks.toggle('portrait-metadata-top', val)
|
||||
});
|
||||
|
||||
this.settings.add('layout.show-portrait-chat', {
|
||||
requires: ['layout.use-portrait', 'layout.portrait-extra-height', 'layout.portrait-extra-width'],
|
||||
process() {
|
||||
|
@ -168,6 +184,8 @@ export default class Layout extends Module {
|
|||
|
||||
this.css_tweaks.toggle('portrait', this.settings.get('layout.inject-portrait'));
|
||||
this.css_tweaks.toggle('portrait-swapped', this.settings.get('layout.use-portrait-swapped'));
|
||||
this.css_tweaks.toggle('portrait-metadata', this.settings.get('layout.use-portrait-meta'));
|
||||
this.css_tweaks.toggle('portrait-metadata-top', this.settings.get('layout.use-portrait-meta-top'));
|
||||
this.css_tweaks.setVariable('portrait-extra-width', `${this.settings.get('layout.portrait-extra-width')}rem`);
|
||||
this.css_tweaks.setVariable('portrait-extra-height', `${this.settings.get('layout.portrait-extra-height')}rem`);
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ import {DEBUG} from 'utilities/constants';
|
|||
import {SiteModule} from 'utilities/module';
|
||||
import {createElement, ClickOutside, setChildren} from 'utilities/dom';
|
||||
|
||||
import Twilight from 'site';
|
||||
|
||||
|
||||
export default class MenuButton extends SiteModule {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
@ -35,6 +38,12 @@ export default class MenuButton extends SiteModule {
|
|||
changed: () => this.update()
|
||||
});
|
||||
|
||||
this.SunlightDash = this.fine.define(
|
||||
'sunlight-dash',
|
||||
n => n.getIsChannelEditor && n.getIsChannelModerator && n.getIsAdsEnabled && n.getIsSquadStreamsEnabled,
|
||||
Twilight.SUNLIGHT_ROUTES
|
||||
);
|
||||
|
||||
this.NavBar = this.fine.define(
|
||||
'nav-bar',
|
||||
n => n.renderOnsiteNotifications && n.renderTwitchPrimeCrown
|
||||
|
@ -171,12 +180,14 @@ export default class MenuButton extends SiteModule {
|
|||
|
||||
for(const inst of this.MultiController.instances)
|
||||
this.updateButton(inst);
|
||||
|
||||
for(const inst of this.SunlightDash.instances)
|
||||
this.updateButton(inst);
|
||||
}
|
||||
|
||||
|
||||
onEnable() {
|
||||
this.NavBar.ready(() => this.update());
|
||||
|
||||
this.NavBar.on('mount', this.updateButton, this);
|
||||
this.NavBar.on('update', this.updateButton, this);
|
||||
|
||||
|
@ -188,6 +199,10 @@ export default class MenuButton extends SiteModule {
|
|||
this.MultiController.on('mount', this.updateButton, this);
|
||||
this.MultiController.on('update', this.updateButton, this);
|
||||
|
||||
this.SunlightDash.ready(() => this.update());
|
||||
this.SunlightDash.on('mount', this.updateButton, this);
|
||||
this.SunlightDash.on('update', this.updateButton, this);
|
||||
|
||||
this.on(':clicked', () => this.important_update = false);
|
||||
|
||||
this.once(':clicked', this.loadMenu);
|
||||
|
@ -201,6 +216,7 @@ export default class MenuButton extends SiteModule {
|
|||
updateButton(inst) {
|
||||
const root = this.fine.getChildNode(inst);
|
||||
let is_squad = false,
|
||||
is_sunlight = false,
|
||||
container = root && root.querySelector('.top-nav__menu');
|
||||
|
||||
if ( ! container ) {
|
||||
|
@ -219,6 +235,12 @@ export default class MenuButton extends SiteModule {
|
|||
is_squad = true;
|
||||
}
|
||||
|
||||
if ( ! container && inst.getIsAdsEnabled ) {
|
||||
container = root && root.querySelector('.sunlight-top-nav > .tw-flex');
|
||||
if ( container )
|
||||
is_sunlight = true;
|
||||
}
|
||||
|
||||
if ( ! container )
|
||||
return;
|
||||
|
||||
|
@ -242,7 +264,7 @@ export default class MenuButton extends SiteModule {
|
|||
extra_pill = this.formatExtraPill();
|
||||
|
||||
el = (<div
|
||||
class="ffz-top-nav tw-align-self-center tw-flex-grow-0 tw-flex-nowrap tw-flex-shrink-0 tw-mg-x-05 tw-relative"
|
||||
class={`ffz-top-nav tw-align-self-center tw-flex-grow-0 tw-flex-nowrap tw-flex-shrink-0 tw-relative ${is_sunlight ? 'tw-mg-l-05 tw-mg-r-2' : 'tw-mg-x-05'}`}
|
||||
>
|
||||
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||
{btn = (<button
|
||||
|
|
104
src/sites/twitch-twilight/modules/settings_sync.js
Normal file
104
src/sites/twitch-twilight/modules/settings_sync.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
'use strict';
|
||||
|
||||
// ============================================================================
|
||||
// Settings Sync
|
||||
// ============================================================================
|
||||
|
||||
import Module from 'utilities/module';
|
||||
import {createElement} from 'utilities/dom';
|
||||
|
||||
export default class SettingsSync extends Module {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.should_enable = window.location.host !== 'www.twitch.tv';
|
||||
|
||||
this.inject('settings');
|
||||
}
|
||||
|
||||
onEnable() {
|
||||
const frame = this.frame = createElement('iframe');
|
||||
frame.src = '//www.twitch.tv/p/ffz_bridge/';
|
||||
frame.id = 'ffz-settings-bridge';
|
||||
frame.style.width = 0;
|
||||
frame.style.height = 0;
|
||||
|
||||
this.settings.provider.on('set', this.onProviderSet, this);
|
||||
window.addEventListener('message', this.onMessage.bind(this));
|
||||
document.body.appendChild(frame);
|
||||
}
|
||||
|
||||
send(msg) {
|
||||
try {
|
||||
this.frame.contentWindow.postMessage(msg, '*');
|
||||
} catch(err) { this.log.error('send error', err); /* no-op */ }
|
||||
}
|
||||
|
||||
onMessage(event) {
|
||||
const msg = event.data;
|
||||
if ( ! msg || ! msg.ffz_type )
|
||||
return;
|
||||
|
||||
if ( msg.ffz_type === 'ready' )
|
||||
this.send({ffz_type: 'load'});
|
||||
else if ( msg.ffz_type === 'loaded' )
|
||||
this.onLoad(msg.data);
|
||||
else if ( msg.ffz_type === 'change' )
|
||||
this.onChange(msg);
|
||||
else
|
||||
this.log.info('Unknown Message', msg.ffz_type, msg);
|
||||
}
|
||||
|
||||
onLoad(data) {
|
||||
if ( ! data )
|
||||
return;
|
||||
|
||||
const provider = this.settings.provider,
|
||||
old_keys = new Set(provider.keys());
|
||||
|
||||
this.skip = true;
|
||||
|
||||
for(const [key, value] of Object.entries(data)) {
|
||||
old_keys.delete(key);
|
||||
if ( provider.get(key) === value )
|
||||
continue;
|
||||
|
||||
provider.set(key, value);
|
||||
provider.emit('changed', key, value, false);
|
||||
}
|
||||
|
||||
for(const key of old_keys) {
|
||||
provider.delete(key);
|
||||
provider.emit('changed', key, undefined, true);
|
||||
}
|
||||
|
||||
this.skip = false;
|
||||
}
|
||||
|
||||
onProviderSet(key, value, deleted) {
|
||||
if ( this.skip )
|
||||
return;
|
||||
|
||||
this.send({
|
||||
ffz_type: 'change',
|
||||
key,
|
||||
value,
|
||||
deleted
|
||||
});
|
||||
}
|
||||
|
||||
onChange(msg) {
|
||||
const key = msg.key,
|
||||
value = msg.value,
|
||||
deleted = msg.deleted;
|
||||
|
||||
this.skip = true;
|
||||
if ( deleted )
|
||||
this.settings.provider.delete(key);
|
||||
else
|
||||
this.settings.provider.set(key, value);
|
||||
this.skip = false;
|
||||
|
||||
this.settings.provider.emit('changed', key, value, deleted, true);
|
||||
}
|
||||
}
|
|
@ -88,6 +88,11 @@
|
|||
text-align: center;
|
||||
display: inline-flex;
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
& + .ffz-mod-icon {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
@ -125,8 +130,8 @@
|
|||
}
|
||||
|
||||
.ffz--action &,
|
||||
.tw-interactable:hover &,
|
||||
&:hover {
|
||||
.tw-interactable:hover &:not(.disabled),
|
||||
&:not(.disabled):hover {
|
||||
.tw-root--theme-dark &, & {
|
||||
&.tw-c-text-alt-2 {
|
||||
color: inherit !important;
|
||||
|
|
|
@ -45,18 +45,25 @@ export default class FineRouter extends Module {
|
|||
|
||||
_navigateTo(location) {
|
||||
this.log.debug('New Location', location);
|
||||
const path = location.pathname;
|
||||
if ( path === this.location )
|
||||
const host = window.location.host,
|
||||
path = location.pathname;
|
||||
|
||||
if ( path === this.location && host === this.domain )
|
||||
return;
|
||||
|
||||
this.location = path;
|
||||
this.domain = host;
|
||||
this._pickRoute();
|
||||
}
|
||||
|
||||
_pickRoute() {
|
||||
const path = this.location;
|
||||
const path = this.location,
|
||||
host = this.domain;
|
||||
|
||||
for(const route of this.__routes) {
|
||||
if ( route.domain && route.domain !== host )
|
||||
continue;
|
||||
|
||||
const match = route.regex.exec(path);
|
||||
if ( match ) {
|
||||
this.log.debug('Matching Route', route, match);
|
||||
|
@ -105,12 +112,19 @@ export default class FineRouter extends Module {
|
|||
}
|
||||
|
||||
getRouteNames() {
|
||||
for(const route of Object.keys(this.getRoutes()))
|
||||
this.getRouteName(route);
|
||||
|
||||
return this.route_names;
|
||||
}
|
||||
|
||||
getRouteName(route) {
|
||||
if ( ! this.route_names[route] )
|
||||
this.route_names[route] = route.replace(/(^|-)([a-z])/g, (_, spacer, letter) => `${spacer ? ' ' : ''}${letter.toLocaleUpperCase()}`);
|
||||
this.route_names[route] = route
|
||||
.replace(/^dash-([a-z])/, (_, letter) =>
|
||||
`Dashboard: ${letter.toLocaleUpperCase()}`)
|
||||
.replace(/(^|-)([a-z])/g, (_, spacer, letter) =>
|
||||
`${spacer ? ' ' : ''}${letter.toLocaleUpperCase()}`);
|
||||
|
||||
return this.route_names[route];
|
||||
}
|
||||
|
@ -132,11 +146,12 @@ export default class FineRouter extends Module {
|
|||
this.emit(':updated-route-names');
|
||||
}
|
||||
|
||||
route(name, path, process = true) {
|
||||
route(name, path, domain = null, process = true) {
|
||||
if ( typeof name === 'object' ) {
|
||||
domain = path;
|
||||
for(const key in name)
|
||||
if ( has(name, key) )
|
||||
this.route(key, name[key], false);
|
||||
this.route(key, name[key], domain, false);
|
||||
|
||||
if ( process ) {
|
||||
this.__routes.sort((a,b) => b.score - a.score);
|
||||
|
@ -157,6 +172,7 @@ export default class FineRouter extends Module {
|
|||
name,
|
||||
parts,
|
||||
score,
|
||||
domain,
|
||||
regex: tokensToRegExp(parts),
|
||||
url: tokensToFunction(parts)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ const ATTRS = [
|
|||
'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase',
|
||||
'color', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu',
|
||||
'controls', 'coords', 'crossorigin', 'data', 'data-*', 'datetime',
|
||||
'default', 'defer', 'dir', 'dirname', 'disabled', 'download', 'draggable',
|
||||
'default', 'defer', 'dir', 'dirname', 'download', 'draggable',
|
||||
'dropzone', 'enctype', 'for', 'form', 'formaction', 'headers', 'height',
|
||||
'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id',
|
||||
'integrity', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue