mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 15:27:43 +00:00
4.20.49
* Added: Override the "Identity" rendering in Twitch's chat settings menu to apply FFZ badge settings and name color correction. * Added: Insert a "FrankerFaceZ Badge" section within the "Identity" editor. Currently, this only directs users to the website to change their badge. * Fixed: Bugs with corrupt settings causing the emote menu not to function correctly for certain users. * Changed: Rename `Twitch Prime` to `Prime Gaming` in several strings. * Changed: Disable the API stress testing experiment ahead of the server maintenance tomorrow.
This commit is contained in:
parent
32859318b2
commit
c97928fbb7
8 changed files with 164 additions and 44 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.20.48",
|
||||
"version": "4.20.49",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"name": "New API Stress Testing",
|
||||
"description": "Send duplicate requests to the new API server for load testing.",
|
||||
"groups": [
|
||||
{"value": true, "weight": 100},
|
||||
{"value": false, "weight": 0}
|
||||
{"value": true, "weight": 0},
|
||||
{"value": false, "weight": 100}
|
||||
]
|
||||
},
|
||||
"api_links": {
|
||||
|
|
|
@ -228,7 +228,7 @@ export default class Emotes extends Module {
|
|||
}
|
||||
|
||||
getHidden(source) {
|
||||
return this.settings.provider.get(`hidden-emotes.${source}`, []);
|
||||
return this.settings.provider.get(`hidden-emotes.${source}`) || [];
|
||||
}
|
||||
|
||||
setHidden(source, list) {
|
||||
|
|
|
@ -1169,7 +1169,7 @@ export const AddonEmotes = {
|
|||
});
|
||||
|
||||
} else if ( type === EmoteTypes.Prime || type === EmoteTypes.Turbo )
|
||||
source = this.i18n.t('emote.prime', 'Twitch Prime');
|
||||
source = this.i18n.t('emote.prime', 'Prime Gaming');
|
||||
|
||||
else if ( type === EmoteTypes.TwoFactor )
|
||||
source = this.i18n.t('emote.2fa', 'Twitch 2FA Emote');
|
||||
|
|
|
@ -181,6 +181,8 @@ export default class Channel extends Module {
|
|||
if ( inst )
|
||||
this.updatePanelTips(inst);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const el = this.fine.getChildNode(inst);
|
||||
|
@ -337,6 +339,11 @@ export default class Channel extends Module {
|
|||
isWatchParty: watching
|
||||
});
|
||||
|
||||
if ( ! el._ffz_cont || ! props?.channelID ) {
|
||||
this.updateSubscription(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( el._ffz_links && props.channelLogin !== el._ffz_link_login ) {
|
||||
const login = el._ffz_link_login = props.channelLogin;
|
||||
if ( login ) {
|
||||
|
@ -369,11 +376,6 @@ export default class Channel extends Module {
|
|||
el._ffz_links.innerHTML = '';
|
||||
}
|
||||
|
||||
if ( ! el._ffz_cont || ! props?.channelID ) {
|
||||
this.updateSubscription(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: See if we can read this data directly from Apollo's cache.
|
||||
// Also, test how it works with videos and clips.
|
||||
/*const raw_game = el.querySelector('a[data-a-target="stream-game-link"]')?.textContent;
|
||||
|
|
|
@ -675,7 +675,7 @@ export default class EmoteMenu extends Module {
|
|||
if ( data.prime )
|
||||
calendar = {
|
||||
icon: 'crown',
|
||||
message: t.i18n.t('emote-menu.sub-prime', 'This is your free sub with Twitch Prime.\nIt ends in {seconds,humantime}.', {seconds: ends})
|
||||
message: t.i18n.t('emote-menu.sub-prime', 'This is your free sub with Prime Gaming.\nIt ends in {seconds,humantime}.', {seconds: ends})
|
||||
}
|
||||
else if ( data.gift )
|
||||
calendar = {
|
||||
|
@ -1263,7 +1263,7 @@ export default class EmoteMenu extends Module {
|
|||
return out;
|
||||
|
||||
const filtering = input && input.length > 0 && input !== ':',
|
||||
hidden_sets = storage.get('emote-menu.hidden-sets', []);
|
||||
hidden_sets = storage.get('emote-menu.hidden-sets') || [];
|
||||
|
||||
for(const emote_set of sets) {
|
||||
if ( ! visibility_control && hidden_sets.includes(emote_set.key) )
|
||||
|
|
|
@ -731,7 +731,7 @@ other {# messages were deleted by a moderator.}
|
|||
className: 'tw-c-text-base tw-strong'
|
||||
}, user.displayName)),
|
||||
plan: plan.prime ?
|
||||
t.i18n.t('chat.sub.twitch-prime', 'with Twitch Prime') :
|
||||
t.i18n.t('chat.sub.twitch-prime', 'with Prime Gaming') :
|
||||
t.i18n.t('chat.sub.plan', 'at Tier {tier}', {tier})
|
||||
});
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
import Twilight from 'site';
|
||||
import Module from 'utilities/module';
|
||||
|
||||
import { has } from 'utilities/object';
|
||||
import {createElement} from 'utilities/dom';
|
||||
|
||||
export default class SettingsMenu extends Module {
|
||||
constructor(...args) {
|
||||
|
@ -16,6 +15,7 @@ export default class SettingsMenu extends Module {
|
|||
this.inject('settings');
|
||||
this.inject('i18n');
|
||||
this.inject('chat');
|
||||
this.inject('chat.badges');
|
||||
this.inject('site.fine');
|
||||
this.inject('site.web_munch');
|
||||
|
||||
|
@ -24,12 +24,6 @@ export default class SettingsMenu extends Module {
|
|||
n => n.renderUniversalOptions && n.onBadgesChanged,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
/*this.ModSettingsMenu = this.fine.define(
|
||||
'chat-mod-settings',
|
||||
n => n.renderModerationSettingsLink && n.onChatClear,
|
||||
Twilight.CHAT_ROUTES
|
||||
);*/
|
||||
}
|
||||
|
||||
async onEnable() {
|
||||
|
@ -171,35 +165,159 @@ export default class SettingsMenu extends Module {
|
|||
this.SettingsMenu.forceUpdate();
|
||||
});
|
||||
|
||||
/*this.ModSettingsMenu.ready(cls => {
|
||||
const old_render = cls.prototype.render;
|
||||
|
||||
cls.prototype.render = function() {
|
||||
const out = old_render.call(this),
|
||||
children = out?.props?.children?.[0]?.props?.children;
|
||||
|
||||
if ( Array.isArray(children) ) {
|
||||
let i = children.length;
|
||||
while(i--) {
|
||||
const thing = children[i];
|
||||
if ( thing && thing.props && has(thing.props, 'chatPauseSetting') ) {
|
||||
children.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
this.ModSettingsMenu.forceUpdate();
|
||||
})*/
|
||||
this.SettingsMenu.on('update', this.updateSettingsMenu, this);
|
||||
|
||||
this.SettingsMenu.on('unmount', inst => {
|
||||
inst.ffzSettingsClick = null;
|
||||
});
|
||||
}
|
||||
|
||||
updateSettingsMenu(inst) {
|
||||
const el = this.fine.getChildNode(inst);
|
||||
if ( ! el || ! document.contains(el) )
|
||||
return;
|
||||
|
||||
let cont;
|
||||
|
||||
if ( inst.props?.editAppearance ) {
|
||||
this.registerBadgePicker();
|
||||
const name = el.querySelector('span[data-a-target="edit-display-name"]');
|
||||
cont = name && name.closest('.tw-mg-t-1');
|
||||
|
||||
} else
|
||||
cont = el.querySelector('.name-display > div:not([data-test-selector="edit-appearance-button"])');
|
||||
|
||||
if ( ! cont || ! el.contains(cont) )
|
||||
return;
|
||||
|
||||
const user = inst.props?.data?.currentUser,
|
||||
raw_badges = inst.props?.data?.user?.self?.displayBadges;
|
||||
|
||||
if ( ! user || ! user.login || ! Array.isArray(raw_badges) )
|
||||
return;
|
||||
|
||||
const is_intl = user.login && user.displayName && user.displayName.trim().toLowerCase() !== user.login,
|
||||
color = this.parent.colors.process(user.chatColor),
|
||||
badges = {};
|
||||
|
||||
for(const child of cont.children) {
|
||||
if ( ! child?.classList )
|
||||
continue;
|
||||
if ( child.classList.contains('ffz--editor-name') )
|
||||
child.remove();
|
||||
else
|
||||
child.classList.add('tw-hide');
|
||||
}
|
||||
|
||||
for(const badge of raw_badges) {
|
||||
if ( badge?.setID && badge.version )
|
||||
badges[badge.setID] = badge.version;
|
||||
}
|
||||
|
||||
cont.appendChild(createElement('span', {className: 'ffz--editor-name'}, [
|
||||
createElement('span', {
|
||||
className: 'ffz--editor-badges',
|
||||
'data-room-id': inst.props.channelID,
|
||||
'data-room-login': inst.props.channelLogin
|
||||
}, this.badges.render({
|
||||
user,
|
||||
badges,
|
||||
roomID: inst.props.channelID,
|
||||
roomLogin: inst.props.channelLogin
|
||||
}, createElement, true)),
|
||||
|
||||
<span class="tw-strong notranslate" style={{color}}>
|
||||
<span class="name-display__name">{user.displayName || user.login}</span>
|
||||
{is_intl && <span class="intl-name"> ({user.login})</span>}
|
||||
</span>
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
registerBadgePicker() {
|
||||
if ( this.BadgePicker )
|
||||
return;
|
||||
|
||||
this.BadgePicker = this.fine.define(
|
||||
'badge-picker',
|
||||
n => n.onGlobalBadgeClicked && n.onGlobalBadgeKeyPress,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.BadgePicker.on('mount', this.updateBadgePicker, this);
|
||||
this.BadgePicker.on('update', this.updateBadgePicker, this);
|
||||
|
||||
this.BadgePicker.ready((cls, instances) => {
|
||||
for(const inst of instances)
|
||||
this.updateBadgePicker(inst);
|
||||
});
|
||||
}
|
||||
|
||||
updateBadgePicker(inst) {
|
||||
const el = this.fine.getChildNode(inst);
|
||||
if ( ! el ) {
|
||||
// This element doesn't populate right away. React is weird.
|
||||
if ( inst.props?.data?.loading === false && ! inst._ffz_try_again )
|
||||
inst._ffz_try_again = requestAnimationFrame(() =>
|
||||
this.updateBadgePicker(inst));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const cont = el.querySelector('.tw-flex-column');
|
||||
if ( ! cont )
|
||||
return;
|
||||
|
||||
const badges = this.badges.getBadges(inst.props.userID, inst.props.userLogin);
|
||||
let badge;
|
||||
|
||||
for(const b of badges) {
|
||||
const bd = this.badges.badges[b?.id];
|
||||
if ( bd && ! bd.addon )
|
||||
badge = bd;
|
||||
}
|
||||
|
||||
const ffz = cont.querySelector('.ffz--badge-selector');
|
||||
if ( ffz ) {
|
||||
if ( ! badge )
|
||||
ffz.remove();
|
||||
return;
|
||||
|
||||
} else if ( ! badge )
|
||||
return;
|
||||
|
||||
cont.appendChild(<div class="ffz--badge-selector">
|
||||
<p class="tw-pd-x-05">
|
||||
{this.i18n.tList('chat.ffz-badge.about', '{title}: This badge appears globally for users with FrankerFaceZ.', {
|
||||
title: <span class="tw-strong">{this.i18n.t('chat.ffz-badge.title', 'FrankerFaceZ Badge')}</span>
|
||||
})}{' '}
|
||||
{this.i18n.tList('chat.ffz-badge.site', 'Please visit the {website} to change this badge.', {
|
||||
website: (<a href="https://www.frankerfacez.com/donate" class="tw-link" rel="noopener noreferrer" target="_blank">
|
||||
{this.i18n.t('chat.ffz-badge.site-link', 'FrankerFaceZ website')}
|
||||
</a>)
|
||||
})}
|
||||
</p>
|
||||
<div role="radiogroup" class="tw-align-items-center tw-flex tw-flex-wrap tw-mg-b-05 tw-mg-t-05 tw-pd-x-05">
|
||||
<div class="tw-mg-r-1 tw-mg-y-05">
|
||||
<div class="tw-inline-flex">
|
||||
<div class="edit-appearance__badge-chooser edit-appearance__badge-chooser--selected tw-border-radius-small">
|
||||
<img
|
||||
alt={badge.title}
|
||||
src={badge.urls[2]}
|
||||
srcset={`${badge.urls[2]} 1x, ${badge.urls[4]} 2x`}
|
||||
style={{backgroundColor: badge.color || 'transparent'}}
|
||||
role="radio"
|
||||
aria-checked="true"
|
||||
class="tw-full-height tw-full-width"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
closeMenu(inst) {
|
||||
const super_parent = this.fine.searchParent(inst, n => n.setChatInputRef && n.setAutocompleteInputRef, 100),
|
||||
parent = super_parent && this.fine.searchTree(super_parent, n => n.props && n.props.isSettingsOpen && n.onClickSettings);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue