1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-02 17:18:31 +00:00
* Added: Display bits emote rewards in the FFZ Emote Menu alongside subscription emotes.
* Changed: 2FA reward emotes are now labelled as such in their tool-tips.
* Changed: Update the style for the chat pause notice at the bottom of chat to match the latest Twitch style and language.
* Fixed: Disabling hosting not working on Mod View.
* Fixed: Emote sets not being sorted correctly in the FFZ Emote Menu when combining multiple sets (such as multiple subscription tiers) into one section.
* API Changed: The `setting-color-box` component now allows you to open the color picker up and not only down.
This commit is contained in:
SirStendec 2020-04-03 19:30:28 -04:00
parent 99eee7396d
commit 30b9a15c0d
12 changed files with 191 additions and 63 deletions

View file

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

View file

@ -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": 75},
{"value": false, "weight": 25}
{"value": true, "weight": 50},
{"value": false, "weight": 50}
]
}
}

View file

@ -963,6 +963,9 @@ function determineEmoteType(emote) {
if ( emote.setID == 300238151 )
return EmoteTypes.ChannelPoints;
if ( emote.setID == 300374282 )
return EmoteTypes.TwoFactor;
const id = parseInt(emote.setID, 10);
if ( ! isNaN(id) && isFinite(id) && id >= 5e8 )
return EmoteTypes.BitsTier;
@ -983,6 +986,9 @@ function determineSetType(set) {
if ( TWITCH_PRIME_SETS.includes(id) )
return EmoteTypes.Prime;
if ( id == 300374282 )
return EmoteTypes.TwoFactor;
const owner = set.owner;
if ( owner ) {
if ( owner.id == 139075904 )

View file

@ -1059,6 +1059,9 @@ export const AddonEmotes = {
} else if ( type === EmoteTypes.Prime || type === EmoteTypes.Turbo )
source = this.i18n.t('emote.prime', 'Twitch Prime');
else if ( type === EmoteTypes.TwoFactor )
source = this.i18n.t('emote.2fa', 'Twitch 2FA Emote');
else if ( type === EmoteTypes.LimitedTime )
source = this.i18n.t('emote.limited', 'Limited-Time Only Emote');

View file

@ -13,6 +13,7 @@
:id="item.full_key"
ref="control"
:alpha="alpha"
:open-up="openUp"
:nullable="true"
:value="color"
@input="onInput"
@ -73,6 +74,13 @@ export default {
return this.value;
},
openUp() {
if ( this.item.openUp != null )
return this.item.openUp;
return false;
},
alpha() {
if ( this.item.alpha != null )
return this.item.alpha;

View file

@ -56,7 +56,7 @@ export default class Channel extends Module {
this.ChannelPage = this.fine.define(
'channel-page',
n => (n.updateHost && n.updateChannel && n.state && has(n.state, 'hostedChannel')) || (n.getHostedChannelLogin && n.handleHostingChange) || (n.onChatHostingChange && n.state && has(n.state, 'hostMode')),
['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following']
['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following', 'mod-view']
);
this.RaidController = this.fine.define(

View file

@ -6,6 +6,7 @@
import Module from 'utilities/module';
import {get} from 'utilities/object';
import {createElement} from 'utilities/dom';
//import CHANNEL_QUERY from './channel_header_query.gql';
@ -57,6 +58,12 @@ export default class ChannelBar extends Module {
n => n.getTitle && n.getGame && n.renderGame,
['user']
);
this.ModWidget = this.fine.define(
'mod-widget',
n => n.renderToolbar && n.getToolbarControls && n.childContext,
['mod-view']
);
}
onEnable() {
@ -77,6 +84,15 @@ export default class ChannelBar extends Module {
});
/*this.ModWidget.on('mount', this.updateModWidget, this);
this.ModWidget.on('update', this.updateModWidget, this);
this.ModWidget.ready((cls, instances) => {
for(const inst of instances)
this.updateModWidget(inst);
});*/
//this.VideoBar.on('unmount', this.unmountVideoBar, this);
this.VideoBar.on('mount', this.updateVideoBar, this);
this.VideoBar.on('update', this.updateVideoBar, this);
@ -89,6 +105,30 @@ export default class ChannelBar extends Module {
}
/*updateModWidget(inst) {
const container = this.fine.getChildNode(inst);
if ( ! container || ! container.querySelector('.video-player-hosting-ui__container') )
return;
const header = container.querySelector('.mod-view-panel-header');
if ( ! header )
return;
let cont = header.querySelector('.ffz--stat-container');
if ( ! cont ) {
cont = <div class="ffz--stat-container tw-pd-l-05"></div>;
const contcont = header.querySelector(':scope > div:first-child > div');
if ( ! contcont )
return;
contcont.appendChild(cont);
}
this.log.info('mod-widget', inst, cont);
}*/
updateVideoBar(inst) {
const container = this.fine.getChildNode(inst),
timestamp = container && container.querySelector('[data-test-selector="date"]');

View file

@ -637,11 +637,17 @@ export default class EmoteMenu extends Module {
return;
const locked = emote.locked && (! lock || ! lock.emotes.has(emote.id)),
emote_lock = locked && data.locks && data.locks[emote.set_id],
sellout = emote_lock ? (data.all_locked ?
t.i18n.t('emote-menu.emote-sub', 'Subscribe for {price} to unlock this emote.', emote_lock) :
t.i18n.t('emote-menu.emote-up', 'Upgrade your sub to {price} to unlock this emote.', emote_lock)
) : '';
emote_lock = locked && data.locks && data.locks[emote.set_id];
let sellout = '';
if ( emote_lock ) {
if ( emote_lock.id === 'cheer' ) {
sellout = t.i18n.t('emote-menu.emote-cheer', 'Cheer an additional {bits_remaining,number} bit{bits_remaining,en_plural} to unlock this emote.', emote_lock);
} else if ( data.all_locked )
sellout = t.i18n.t('emote-menu.emote-sub', 'Subscribe for {price} to unlock this emote.', emote_lock);
else
sellout = t.i18n.t('emote-menu.emote-up', 'Upgrade your sub to {price} to unlock this emote.', emote_lock);
}
return this.renderEmote(
emote,
@ -698,14 +704,18 @@ export default class EmoteMenu extends Module {
if ( ! data.all_locked || ! data.locks )
return null;
const lock = data.locks[this.state.unlocked];
const lock = data.locks[this.state.unlocked],
locks = Object.values(data.locks).filter(x => x.id !== 'cheer');
if ( ! locks.length )
return null;
return (<div class="tw-mg-1 tw-border-t tw-pd-t-1 tw-mg-b-0">
{lock ?
t.i18n.t('emote-menu.sub-unlock', 'Subscribe for {price} to unlock {count,number} emote{count,en_plural}', {price: lock.price, count: lock.emotes.size}) :
t.i18n.t('emote-menu.sub-basic', 'Subscribe to unlock some emotes')}
<div class="ffz--sub-buttons tw-mg-t-05">
{Object.values(data.locks).map(lock => (<a
{locks.map(lock => (<a
key={lock.price}
class="tw-button tw-border-radius-none"
href={lock.url}
@ -1282,9 +1292,15 @@ export default class EmoteMenu extends Module {
if ( a.locked && ! b.locked ) return 1;
if ( sort_tiers || a.locked || b.locked ) {
if ( COLLATOR ) {
const result = COLLATOR.compare(a.set_id, b.set_id);
if ( result != 0 )
return result;
} else {
if ( a.set_id < b.set_id ) return -1;
if ( a.set_id > b.set_id ) return 1;
}
}
return sorter(a,b);
}
@ -1494,9 +1510,10 @@ export default class EmoteMenu extends Module {
// Now we handle the current Channel's emotes.
const user = props.channel_data && props.channel_data.user,
products = user && user.subscriptionProducts;
products = user && user.subscriptionProducts,
bits = user?.cheer?.badgeTierEmotes;
if ( Array.isArray(products) ) {
if ( Array.isArray(products) || Array.isArray(bits) ) {
const badge = t.badges.getTwitchBadge('subscriber', '0', user.id, user.login),
emotes = [],
locks = {},
@ -1506,13 +1523,14 @@ export default class EmoteMenu extends Module {
image: badge && badge.image1x,
image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
icon: 'twitch',
title: t.i18n.t('emote-menu.sub-set', 'Subscriber Emotes'),
title: t.i18n.t('emote-menu.main-set', 'Channel Emotes'),
source: t.i18n.t('emote-menu.twitch', 'Twitch'),
emotes,
locks,
all_locked: true
};
if ( Array.isArray(products) ) {
for(const product of products) {
if ( ! product || ! Array.isArray(product.emotes) )
continue;
@ -1579,6 +1597,53 @@ export default class EmoteMenu extends Module {
lock_set.add(id);
}
}
}
if ( Array.isArray(bits) ) {
for(const emote of bits) {
if ( ! emote || ! emote.id || ! emote.bitsBadgeTierSummary )
continue;
const id = emote.id,
set_id = emote.setID,
summary = emote.bitsBadgeTierSummary,
locked = ! twitch_seen.has(id) && ! summary.self?.isUnlocked;
// If the emote isn't unlocked, store data about that in the
// section so we can show appropriate UI to let people know
// that the emote isn't unlocked.
if ( locked )
locks[set_id] = {
set_id,
id: 'cheer',
price: null,
bits: summary.threshold,
bits_remaining: summary.self?.numberOfBitsUntilUnlock ?? summary.threshold,
emotes: new Set([emote.id])
}
const base = `${TWITCH_EMOTE_BASE}${id}`,
is_fav = twitch_favorites.includes(id);
const em = {
provider: 'twitch',
id,
set_id,
name: emote.token,
locked,
src: `${base}/1.0`,
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
favorite: is_fav
};
emotes.push(em);
if ( ! locked && is_fav )
favorites.push(em);
twitch_seen.add(id);
}
}
if ( emotes.length ) {
emotes.sort(sort_emotes);

View file

@ -588,18 +588,23 @@ export default class Scroller extends Module {
f === 9 ? t.i18n.t('key.shift_mouse', 'Shift or Mouse') :
t.i18n.t('key.mouse', 'Mouse Movement');
msg = t.i18n.t('chat.paused', '(Chat Paused Due to {reason})', {reason});
msg = t.i18n.t('chat.paused', 'Chat Paused Due to {reason}', {reason});
cls = 'ffz--freeze-indicator';
} else if ( this.state.isAutoScrolling )
return null;
else
msg = t.i18n.t('chat.messages-below', 'More messages below.');
msg = t.i18n.t('chat.messages-below', 'Chat Paused Due to Scroll');
return createElement('div', {
className: `chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-full-width tw-justify-content-center tw-pd-05 ${cls}`,
className: `chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-justify-content-center tw-mg-b-1 tw-pd-x-2 tw-pd-y-05 ${cls}`,
onClick: this.ffzFastResume
}, createElement('div', null, msg));
/*return createElement('div', {
className: `chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-full-width tw-justify-content-center tw-pd-05 ${cls}`,
onClick: this.ffzFastResume
}, createElement('div', null, msg));*/
}
cls.prototype.smoothScrollBottom = function() {

View file

@ -494,7 +494,7 @@ export default class Fine extends Module {
if ( idx !== -1 )
this._waiting.splice(idx, 1);
this.log.info(`Found class for "${w.name}" at depth ${d.depth}`);
this.log.debug(`Found class for "${w.name}" at depth ${d.depth}`);
w._set(d.cls, d.instances);
}
}

View file

@ -85,7 +85,7 @@ export default class WebMunch extends Module {
webpackJsonpv3(chunk_ids, modules) {
const names = chunk_ids.map(x => this._module_names[x] || x).join(', ');
this.log.info(`Twitch Chunk Loaded: ${chunk_ids} (${names})`);
this.log.debug(`Twitch Chunk Loaded: ${chunk_ids} (${names})`);
this.log.debug(`Modules: ${Object.keys(modules)}`);
const res = this._original_loader.apply(window, arguments); // eslint-disable-line prefer-rest-params
@ -129,7 +129,7 @@ export default class WebMunch extends Module {
modules = data[1],
names = Array.isArray(chunk_ids) && chunk_ids.map(x => this._module_names[x] || x).join(', ');
this.log.info(`Twitch Chunk Loaded: ${chunk_ids} (${names})`);
this.log.debug(`Twitch Chunk Loaded: ${chunk_ids} (${names})`);
this.log.debug(`Modules: ${Object.keys(modules)}`);
if ( modules )

View file

@ -109,5 +109,6 @@ export const EmoteTypes = make_enum(
'Unavailable',
'Subscription',
'BitsTier',
'Global'
'Global',
'TwoFactor'
);