mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-08 15:20:55 +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",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.15.5",
|
"version": "4.16.0",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -82,7 +82,9 @@ class FFZBridge extends Module {
|
||||||
ffz_type: 'loaded',
|
ffz_type: 'loaded',
|
||||||
data: out
|
data: out
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
} else if ( msg.ffz_type === 'change' )
|
||||||
|
this.onChange(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(msg) { // eslint-disable-line class-methods-use-this
|
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 */ }
|
} 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) {
|
onProviderChange(key, value, deleted) {
|
||||||
this.send({
|
this.send({
|
||||||
ffz_type: 'change',
|
ffz_type: 'change',
|
||||||
|
|
|
@ -437,12 +437,11 @@ export default class Actions extends Module {
|
||||||
if ( ! data || ! data.action || ! data.appearance )
|
if ( ! data || ! data.action || ! data.appearance )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ap = data.appearance || {},
|
let ap = data.appearance || {};
|
||||||
disp = data.display || {},
|
const disp = data.display || {},
|
||||||
|
act = this.actions[data.action];
|
||||||
|
|
||||||
def = this.renderers[ap.type];
|
if ( ! act || disp.disabled ||
|
||||||
|
|
||||||
if ( ! def || disp.disabled ||
|
|
||||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||||
(disp.mod != null && disp.mod !== (current_user ? !!current_user.mod : false)) ||
|
(disp.mod != null && disp.mod !== (current_user ? !!current_user.mod : false)) ||
|
||||||
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : 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) )
|
(disp.followersOnly != null && disp.followersOnly !== current_room.followersOnly) )
|
||||||
continue;
|
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,
|
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),
|
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||||
contents = def.render.call(this, ap, createElement, color);
|
contents = def.render.call(this, ap, createElement, color);
|
||||||
|
|
||||||
actions.push(<button
|
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-tooltip-type="action"
|
||||||
data-action={data.action}
|
data-action={data.action}
|
||||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
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 )
|
} else if ( ! data.action || ! data.appearance )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ap = data.appearance || {},
|
let ap = data.appearance || {};
|
||||||
disp = data.display || {},
|
const disp = data.display || {},
|
||||||
|
act = this.actions[data.action];
|
||||||
|
|
||||||
def = this.renderers[ap.type];
|
if ( ! act || disp.disabled ||
|
||||||
|
|
||||||
if ( ! def || disp.disabled ||
|
|
||||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||||
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
||||||
(disp.staff != null && disp.staff !== (u ? !!u.staff : false)) ||
|
(disp.staff != null && disp.staff !== (u ? !!u.staff : false)) ||
|
||||||
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
||||||
continue;
|
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,
|
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),
|
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||||
contents = def.render.call(this, ap, createElement, color);
|
contents = def.render.call(this, ap, createElement, color);
|
||||||
|
|
||||||
|
@ -572,7 +592,8 @@ export default class Actions extends Module {
|
||||||
lines.push(line = []);
|
lines.push(line = []);
|
||||||
|
|
||||||
const btn = (<button
|
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-tooltip-type="action"
|
||||||
data-action={data.action}
|
data-action={data.action}
|
||||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||||
|
@ -631,20 +652,30 @@ export default class Actions extends Module {
|
||||||
if ( ! data.action || ! data.appearance )
|
if ( ! data.action || ! data.appearance )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ap = data.appearance || {},
|
let ap = data.appearance || {};
|
||||||
disp = data.display || {},
|
const disp = data.display || {},
|
||||||
keys = disp.keys,
|
keys = disp.keys,
|
||||||
|
act = this.actions[data.action];
|
||||||
|
|
||||||
def = this.renderers[ap.type];
|
if ( ! act || disp.disabled ||
|
||||||
|
|
||||||
if ( ! def || disp.disabled ||
|
|
||||||
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
(disp.mod_icons != null && disp.mod_icons !== !!mod_icons) ||
|
||||||
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
(disp.mod != null && disp.mod !== (current_level > msg_level)) ||
|
||||||
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : false)) ||
|
(disp.staff != null && disp.staff !== (current_user ? !!current_user.staff : false)) ||
|
||||||
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
|
||||||
continue;
|
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,
|
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),
|
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||||
contents = def.render.call(this, ap, createElement, color);
|
contents = def.render.call(this, ap, createElement, color);
|
||||||
|
|
||||||
|
@ -655,7 +686,8 @@ export default class Actions extends Module {
|
||||||
|
|
||||||
had_action = true;
|
had_action = true;
|
||||||
list.push(<button
|
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-tooltip-type="action"
|
||||||
data-action={data.action}
|
data-action={data.action}
|
||||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||||
|
@ -799,6 +831,9 @@ export default class Actions extends Module {
|
||||||
if ( ! data )
|
if ( ! data )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( target.classList.contains('disabled') )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! data.definition.click ) {
|
if ( ! data.definition.click ) {
|
||||||
if ( data.definition.context )
|
if ( data.definition.context )
|
||||||
return this.handleContext(event);
|
return this.handleContext(event);
|
||||||
|
@ -823,6 +858,9 @@ export default class Actions extends Module {
|
||||||
if ( ! data )
|
if ( ! data )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( target.classList.contains('disabled') )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( target._ffz_tooltip$0 )
|
if ( target._ffz_tooltip$0 )
|
||||||
target._ffz_tooltip$0.hide();
|
target._ffz_tooltip$0.hide();
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,8 @@ export default class Metadata extends Module {
|
||||||
subtitle: () => this.i18n.t('metadata.uptime.subtitle', 'Uptime'),
|
subtitle: () => this.i18n.t('metadata.uptime.subtitle', 'Uptime'),
|
||||||
|
|
||||||
tooltip(data) {
|
tooltip(data) {
|
||||||
|
console.log('tool-tip');
|
||||||
|
|
||||||
if ( ! data.created )
|
if ( ! data.created )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -444,8 +446,7 @@ export default class Metadata extends Module {
|
||||||
if ( ! label )
|
if ( ! label )
|
||||||
return destroy();
|
return destroy();
|
||||||
|
|
||||||
const tooltip = maybe_call(def.tooltip, this, data),
|
const order = maybe_call(def.order, this, data),
|
||||||
order = maybe_call(def.order, this, data),
|
|
||||||
color = maybe_call(def.color, this, data) || '';
|
color = maybe_call(def.color, this, data) || '';
|
||||||
|
|
||||||
if ( ! el ) {
|
if ( ! el ) {
|
||||||
|
@ -468,7 +469,7 @@ export default class Metadata extends Module {
|
||||||
el = (<div
|
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'}`}
|
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}
|
data-key={key}
|
||||||
tip_content={tooltip}
|
tip_content={null}
|
||||||
>
|
>
|
||||||
{btn = (<button
|
{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'}`}
|
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
|
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'}`}
|
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}
|
data-key={key}
|
||||||
tip_content={tooltip}
|
tip_content={null}
|
||||||
>
|
>
|
||||||
<div class="tw-align-items-center tw-flex tw-flex-grow-0 tw-justify-center">
|
<div class="tw-align-items-center tw-flex tw-flex-grow-0 tw-justify-center">
|
||||||
{icon}
|
{icon}
|
||||||
|
@ -592,7 +593,7 @@ export default class Metadata extends Module {
|
||||||
el = (<div
|
el = (<div
|
||||||
class="tw-align-items-center tw-inline-flex tw-relative tw-tooltip-wrapper ffz-stat tw-stat tw-mg-l-1"
|
class="tw-align-items-center tw-inline-flex tw-relative tw-tooltip-wrapper ffz-stat tw-stat tw-mg-l-1"
|
||||||
data-key={key}
|
data-key={key}
|
||||||
tip_content={tooltip}
|
tip_content={null}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
{stat = <span class={`${icon ? 'tw-mg-l-05 ' : ''}ffz-stat-text tw-stat__value`} />}
|
{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,
|
logger: this.log,
|
||||||
live: false,
|
live: false,
|
||||||
html: true,
|
html: true,
|
||||||
content: () => el.tip_content,
|
content: () => maybe_call(def.tooltip, this, el._ffz_data),
|
||||||
onShow: (t, tip) => el.tip = tip,
|
onShow: (t, tip) => el.tip = tip,
|
||||||
onHide: () => el.tip = null,
|
onHide: () => {
|
||||||
|
el.tip = null;
|
||||||
|
el.tip_content = null;
|
||||||
|
},
|
||||||
popper: {
|
popper: {
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
modifiers: {
|
modifiers: {
|
||||||
|
@ -655,10 +659,12 @@ export default class Metadata extends Module {
|
||||||
if ( el._ffz_order !== order )
|
if ( el._ffz_order !== order )
|
||||||
el.style.order = el._ffz_order = order;
|
el.style.order = el._ffz_order = order;
|
||||||
|
|
||||||
if ( el.tip_content !== tooltip ) {
|
if ( el.tip ) {
|
||||||
el.tip_content = tooltip;
|
const tooltip = maybe_call(def.tooltip, this, data);
|
||||||
if ( el.tip )
|
if ( el.tip_content !== tooltip ) {
|
||||||
|
el.tip_content = tooltip;
|
||||||
setChildren(el.tip.element, tooltip);
|
setChildren(el.tip.element, tooltip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,12 +180,14 @@ export class LocalStorageProvider extends SettingsProvider {
|
||||||
this._cached.set(key, value);
|
this._cached.set(key, value);
|
||||||
localStorage.setItem(this.prefix + key, JSON.stringify(value));
|
localStorage.setItem(this.prefix + key, JSON.stringify(value));
|
||||||
this.broadcast({type: 'set', key});
|
this.broadcast({type: 'set', key});
|
||||||
|
this.emit('set', key, value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key) {
|
delete(key) {
|
||||||
this._cached.delete(key);
|
this._cached.delete(key);
|
||||||
localStorage.removeItem(this.prefix + key);
|
localStorage.removeItem(this.prefix + key);
|
||||||
this.broadcast({type: 'delete', key});
|
this.broadcast({type: 'delete', key});
|
||||||
|
this.emit('set', key, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
has(key) {
|
has(key) {
|
||||||
|
|
|
@ -42,8 +42,11 @@ export default class Twilight extends BaseSite {
|
||||||
this.populateModules();
|
this.populateModules();
|
||||||
|
|
||||||
this.web_munch.known(Twilight.KNOWN_MODULES);
|
this.web_munch.known(Twilight.KNOWN_MODULES);
|
||||||
|
|
||||||
this.router.route(Twilight.ROUTES);
|
this.router.route(Twilight.ROUTES);
|
||||||
this.router.routeName(Twilight.ROUTE_NAMES);
|
this.router.routeName(Twilight.ROUTE_NAMES);
|
||||||
|
|
||||||
|
this.router.route(Twilight.DASH_ROUTES, 'dashboard.twitch.tv');
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -205,7 +208,8 @@ Twilight.CHAT_ROUTES = [
|
||||||
'dash',
|
'dash',
|
||||||
'embed-chat',
|
'embed-chat',
|
||||||
'squad',
|
'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 = {
|
Twilight.ROUTES = {
|
||||||
'front-page': '/',
|
'front-page': '/',
|
||||||
'collection': '/collections/:collectionID',
|
'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)
|
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', {
|
this.settings.add('layout.show-portrait-chat', {
|
||||||
requires: ['layout.use-portrait', 'layout.portrait-extra-height', 'layout.portrait-extra-width'],
|
requires: ['layout.use-portrait', 'layout.portrait-extra-height', 'layout.portrait-extra-width'],
|
||||||
process() {
|
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', this.settings.get('layout.inject-portrait'));
|
||||||
this.css_tweaks.toggle('portrait-swapped', this.settings.get('layout.use-portrait-swapped'));
|
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-width', `${this.settings.get('layout.portrait-extra-width')}rem`);
|
||||||
this.css_tweaks.setVariable('portrait-extra-height', `${this.settings.get('layout.portrait-extra-height')}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 {SiteModule} from 'utilities/module';
|
||||||
import {createElement, ClickOutside, setChildren} from 'utilities/dom';
|
import {createElement, ClickOutside, setChildren} from 'utilities/dom';
|
||||||
|
|
||||||
|
import Twilight from 'site';
|
||||||
|
|
||||||
|
|
||||||
export default class MenuButton extends SiteModule {
|
export default class MenuButton extends SiteModule {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -35,6 +38,12 @@ export default class MenuButton extends SiteModule {
|
||||||
changed: () => this.update()
|
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(
|
this.NavBar = this.fine.define(
|
||||||
'nav-bar',
|
'nav-bar',
|
||||||
n => n.renderOnsiteNotifications && n.renderTwitchPrimeCrown
|
n => n.renderOnsiteNotifications && n.renderTwitchPrimeCrown
|
||||||
|
@ -171,12 +180,14 @@ export default class MenuButton extends SiteModule {
|
||||||
|
|
||||||
for(const inst of this.MultiController.instances)
|
for(const inst of this.MultiController.instances)
|
||||||
this.updateButton(inst);
|
this.updateButton(inst);
|
||||||
|
|
||||||
|
for(const inst of this.SunlightDash.instances)
|
||||||
|
this.updateButton(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
this.NavBar.ready(() => this.update());
|
this.NavBar.ready(() => this.update());
|
||||||
|
|
||||||
this.NavBar.on('mount', this.updateButton, this);
|
this.NavBar.on('mount', this.updateButton, this);
|
||||||
this.NavBar.on('update', 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('mount', this.updateButton, this);
|
||||||
this.MultiController.on('update', 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.on(':clicked', () => this.important_update = false);
|
||||||
|
|
||||||
this.once(':clicked', this.loadMenu);
|
this.once(':clicked', this.loadMenu);
|
||||||
|
@ -201,6 +216,7 @@ export default class MenuButton extends SiteModule {
|
||||||
updateButton(inst) {
|
updateButton(inst) {
|
||||||
const root = this.fine.getChildNode(inst);
|
const root = this.fine.getChildNode(inst);
|
||||||
let is_squad = false,
|
let is_squad = false,
|
||||||
|
is_sunlight = false,
|
||||||
container = root && root.querySelector('.top-nav__menu');
|
container = root && root.querySelector('.top-nav__menu');
|
||||||
|
|
||||||
if ( ! container ) {
|
if ( ! container ) {
|
||||||
|
@ -219,6 +235,12 @@ export default class MenuButton extends SiteModule {
|
||||||
is_squad = true;
|
is_squad = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ! container && inst.getIsAdsEnabled ) {
|
||||||
|
container = root && root.querySelector('.sunlight-top-nav > .tw-flex');
|
||||||
|
if ( container )
|
||||||
|
is_sunlight = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! container )
|
if ( ! container )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -242,7 +264,7 @@ export default class MenuButton extends SiteModule {
|
||||||
extra_pill = this.formatExtraPill();
|
extra_pill = this.formatExtraPill();
|
||||||
|
|
||||||
el = (<div
|
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">
|
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||||
{btn = (<button
|
{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;
|
text-align: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
& + .ffz-mod-icon {
|
& + .ffz-mod-icon {
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +130,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz--action &,
|
.ffz--action &,
|
||||||
.tw-interactable:hover &,
|
.tw-interactable:hover &:not(.disabled),
|
||||||
&:hover {
|
&:not(.disabled):hover {
|
||||||
.tw-root--theme-dark &, & {
|
.tw-root--theme-dark &, & {
|
||||||
&.tw-c-text-alt-2 {
|
&.tw-c-text-alt-2 {
|
||||||
color: inherit !important;
|
color: inherit !important;
|
||||||
|
|
|
@ -45,18 +45,25 @@ export default class FineRouter extends Module {
|
||||||
|
|
||||||
_navigateTo(location) {
|
_navigateTo(location) {
|
||||||
this.log.debug('New Location', location);
|
this.log.debug('New Location', location);
|
||||||
const path = location.pathname;
|
const host = window.location.host,
|
||||||
if ( path === this.location )
|
path = location.pathname;
|
||||||
|
|
||||||
|
if ( path === this.location && host === this.domain )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.location = path;
|
this.location = path;
|
||||||
|
this.domain = host;
|
||||||
this._pickRoute();
|
this._pickRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
_pickRoute() {
|
_pickRoute() {
|
||||||
const path = this.location;
|
const path = this.location,
|
||||||
|
host = this.domain;
|
||||||
|
|
||||||
for(const route of this.__routes) {
|
for(const route of this.__routes) {
|
||||||
|
if ( route.domain && route.domain !== host )
|
||||||
|
continue;
|
||||||
|
|
||||||
const match = route.regex.exec(path);
|
const match = route.regex.exec(path);
|
||||||
if ( match ) {
|
if ( match ) {
|
||||||
this.log.debug('Matching Route', route, match);
|
this.log.debug('Matching Route', route, match);
|
||||||
|
@ -105,12 +112,19 @@ export default class FineRouter extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
getRouteNames() {
|
getRouteNames() {
|
||||||
|
for(const route of Object.keys(this.getRoutes()))
|
||||||
|
this.getRouteName(route);
|
||||||
|
|
||||||
return this.route_names;
|
return this.route_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRouteName(route) {
|
getRouteName(route) {
|
||||||
if ( ! this.route_names[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];
|
return this.route_names[route];
|
||||||
}
|
}
|
||||||
|
@ -132,11 +146,12 @@ export default class FineRouter extends Module {
|
||||||
this.emit(':updated-route-names');
|
this.emit(':updated-route-names');
|
||||||
}
|
}
|
||||||
|
|
||||||
route(name, path, process = true) {
|
route(name, path, domain = null, process = true) {
|
||||||
if ( typeof name === 'object' ) {
|
if ( typeof name === 'object' ) {
|
||||||
|
domain = path;
|
||||||
for(const key in name)
|
for(const key in name)
|
||||||
if ( has(name, key) )
|
if ( has(name, key) )
|
||||||
this.route(key, name[key], false);
|
this.route(key, name[key], domain, false);
|
||||||
|
|
||||||
if ( process ) {
|
if ( process ) {
|
||||||
this.__routes.sort((a,b) => b.score - a.score);
|
this.__routes.sort((a,b) => b.score - a.score);
|
||||||
|
@ -157,6 +172,7 @@ export default class FineRouter extends Module {
|
||||||
name,
|
name,
|
||||||
parts,
|
parts,
|
||||||
score,
|
score,
|
||||||
|
domain,
|
||||||
regex: tokensToRegExp(parts),
|
regex: tokensToRegExp(parts),
|
||||||
url: tokensToFunction(parts)
|
url: tokensToFunction(parts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ const ATTRS = [
|
||||||
'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase',
|
'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase',
|
||||||
'color', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu',
|
'color', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu',
|
||||||
'controls', 'coords', 'crossorigin', 'data', 'data-*', 'datetime',
|
'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',
|
'dropzone', 'enctype', 'for', 'form', 'formaction', 'headers', 'height',
|
||||||
'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id',
|
'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id',
|
||||||
'integrity', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang',
|
'integrity', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue