mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-15 17:46:55 +00:00
4.20.38
* Fixed: Emotes not appearing in Chat on Videos. * Fixed: Extra, unnecessary `Pause Chat` section appearing in the Twitch chat settings menu. * Fixed: Hide the number of unread whispers when hiding the whispers button. * Fixed: The alternative live viewer display sometimes incorrectly displaying zero when loading the page. * Fixed: Precision issue when displaying localized numbers in some cases.
This commit is contained in:
parent
943e0fdc18
commit
472f9472ee
8 changed files with 93 additions and 14 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.20.37",
|
"version": "4.20.38",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1388,9 +1388,15 @@ export default class Chat extends Module {
|
||||||
ret = `${content.alt}${content.cheerAmount}`;
|
ret = `${content.alt}${content.cheerAmount}`;
|
||||||
|
|
||||||
} else if ( content.images ) {
|
} else if ( content.images ) {
|
||||||
const url = (content.images.themed ? content.images.dark : content.images.sources),
|
const url = (content.images.themed ? content.images.dark : content.images.sources);
|
||||||
match = url && /\/emoticons\/v1\/(\d+)\/[\d.]+$/.exec(url['1x']),
|
let id = content.emoteID;
|
||||||
|
if ( ! id ) {
|
||||||
|
const match = url && (
|
||||||
|
/\/emoticons\/v1\/(\d+)\/[\d.]+$/.exec(url['1x']) ||
|
||||||
|
/\/emoticons\/v2\/(\d+)\//.exec(url['1x'])
|
||||||
|
);
|
||||||
id = match && match[1];
|
id = match && match[1];
|
||||||
|
}
|
||||||
|
|
||||||
ret = content.alt;
|
ret = content.alt;
|
||||||
|
|
||||||
|
|
|
@ -369,6 +369,24 @@ export default class Channel extends Module {
|
||||||
return;
|
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;
|
||||||
|
if ( ! el._ffz_game_cache_updating && el._ffz_game_cache !== raw_game ) {
|
||||||
|
el._ffz_game_cache_updating = true;
|
||||||
|
el._ffz_game_cache = raw_game;
|
||||||
|
|
||||||
|
this.twitch_data.getUserGame(props.channelID, props.channelLogin).then(game => {
|
||||||
|
el._ffz_game_cache_updating = false;
|
||||||
|
this.settings.updateContext({
|
||||||
|
category: game?.displayName,
|
||||||
|
categoryID: game?.id
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
el._ffz_game_cache_updating = false;
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
const other_props = react.child.child?.child?.child?.child?.child?.child?.child?.child?.child?.memoizedProps,
|
const other_props = react.child.child?.child?.child?.child?.child?.child?.child?.child?.child?.memoizedProps,
|
||||||
title = other_props?.title;
|
title = other_props?.title;
|
||||||
|
|
||||||
|
@ -438,8 +456,19 @@ export default class Channel extends Module {
|
||||||
getViewerCount: () => {
|
getViewerCount: () => {
|
||||||
const thing = cont.querySelector('p[data-a-target="animated-channel-viewers-count"]'),
|
const thing = cont.querySelector('p[data-a-target="animated-channel-viewers-count"]'),
|
||||||
r = thing && this.fine.getReactInstance(thing),
|
r = thing && this.fine.getReactInstance(thing),
|
||||||
p = r?.memoizedProps?.children?.props;
|
c = r?.memoizedProps?.children;
|
||||||
|
|
||||||
|
// Sometimes, on early loads, the animated element
|
||||||
|
// is actually a static string.
|
||||||
|
if ( typeof c === 'string' && /^[0-9,.]+$/.test(c) ) {
|
||||||
|
try {
|
||||||
|
const val = parseInt(c.replace(/,/, ''), 10);
|
||||||
|
if ( ! isNaN(val) && isFinite(val) && val > 0 )
|
||||||
|
return val;
|
||||||
|
} catch(err) { /* no-op */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = c?.props;
|
||||||
if ( p && p.value != null )
|
if ( p && p.value != null )
|
||||||
return p.value;
|
return p.value;
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@ export default class SettingsMenu extends Module {
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ModSettingsMenu = this.fine.define(
|
/*this.ModSettingsMenu = this.fine.define(
|
||||||
'chat-mod-settings',
|
'chat-mod-settings',
|
||||||
n => n.renderModerationSettingsLink && n.onChatClear,
|
n => n.renderModerationSettingsLink && n.onChatClear,
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
|
@ -71,7 +71,7 @@ export default class SettingsMenu extends Module {
|
||||||
</div>}
|
</div>}
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
const f = t.chat.context.get('chat.scroller.freeze'),
|
/*const f = t.chat.context.get('chat.scroller.freeze'),
|
||||||
reason = f === 2 ? t.i18n.t('key.ctrl', 'Ctrl Key') :
|
reason = f === 2 ? t.i18n.t('key.ctrl', 'Ctrl Key') :
|
||||||
f === 3 ? t.i18n.t('key.meta', 'Meta Key') :
|
f === 3 ? t.i18n.t('key.meta', 'Meta Key') :
|
||||||
f === 4 ? t.i18n.t('key.alt', 'Alt Key') :
|
f === 4 ? t.i18n.t('key.alt', 'Alt Key') :
|
||||||
|
@ -96,7 +96,7 @@ export default class SettingsMenu extends Module {
|
||||||
<figure class="tw-svg ffz-i-right-dir" />
|
<figure class="tw-svg ffz-i-right-dir" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>);
|
</div>);*/
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ export default class SettingsMenu extends Module {
|
||||||
this.SettingsMenu.forceUpdate();
|
this.SettingsMenu.forceUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ModSettingsMenu.ready(cls => {
|
/*this.ModSettingsMenu.ready(cls => {
|
||||||
const old_render = cls.prototype.render;
|
const old_render = cls.prototype.render;
|
||||||
|
|
||||||
cls.prototype.render = function() {
|
cls.prototype.render = function() {
|
||||||
|
@ -193,7 +193,7 @@ export default class SettingsMenu extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ModSettingsMenu.forceUpdate();
|
this.ModSettingsMenu.forceUpdate();
|
||||||
})
|
})*/
|
||||||
|
|
||||||
this.SettingsMenu.on('unmount', inst => {
|
this.SettingsMenu.on('unmount', inst => {
|
||||||
inst.ffzSettingsClick = null;
|
inst.ffzSettingsClick = null;
|
||||||
|
|
|
@ -33,7 +33,7 @@ const CLASSES = {
|
||||||
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
||||||
|
|
||||||
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard',
|
'pinned-cheer': '.pinned-cheer,.pinned-cheer-v2,.channel-leaderboard',
|
||||||
'whispers': 'body .whispers-open-threads,.tw-core-button[data-a-target="whisper-box-button"]',
|
'whispers': 'body .whispers-open-threads,.tw-core-button[data-a-target="whisper-box-button"],.whispers__pill',
|
||||||
|
|
||||||
'dir-live-ind': '.live-channel-card[data-ffz-type="live"] .tw-channel-status-text-indicator, article[data-ffz-type="live"] .tw-channel-status-text-indicator',
|
'dir-live-ind': '.live-channel-card[data-ffz-type="live"] .tw-channel-status-text-indicator, article[data-ffz-type="live"] .tw-channel-status-text-indicator',
|
||||||
'profile-hover': '.preview-card .tw-relative:hover .ffz-channel-avatar',
|
'profile-hover': '.preview-card .tw-relative:hover .ffz-channel-avatar',
|
||||||
|
|
12
src/utilities/data/user-game.gql
Normal file
12
src/utilities/data/user-game.gql
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
query FFZ_UserGame($id: ID, $login: String) {
|
||||||
|
user(id: $id, login: $login) {
|
||||||
|
id
|
||||||
|
broadcastSettings {
|
||||||
|
id
|
||||||
|
game {
|
||||||
|
id
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,9 +51,9 @@ export const DEFAULT_TYPES = {
|
||||||
|
|
||||||
number(val, node) {
|
number(val, node) {
|
||||||
if ( typeof val !== 'number' ) {
|
if ( typeof val !== 'number' ) {
|
||||||
let new_val = parseInt(val, 10);
|
let new_val = parseFloat(val);
|
||||||
if ( isNaN(new_val) || ! isFinite(new_val) )
|
if ( isNaN(new_val) || ! isFinite(new_val) )
|
||||||
new_val = parseFloat(val);
|
new_val = parseInt(val, 10);
|
||||||
if ( isNaN(new_val) || ! isFinite(new_val) )
|
if ( isNaN(new_val) || ! isFinite(new_val) )
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
|
@ -254,7 +254,16 @@ export default class TranslationCore {
|
||||||
formatNumber(value, format) {
|
formatNumber(value, format) {
|
||||||
let formatter = this.numberFormats.get(format);
|
let formatter = this.numberFormats.get(format);
|
||||||
if ( ! formatter ) {
|
if ( ! formatter ) {
|
||||||
|
if ( this.formats.number[format] )
|
||||||
formatter = new Intl.NumberFormat(this.locale, this.formats.number[format]);
|
formatter = new Intl.NumberFormat(this.locale, this.formats.number[format]);
|
||||||
|
else if ( typeof format === 'number' )
|
||||||
|
formatter = new Intl.NumberFormat(this.locale, {
|
||||||
|
minimumFractionDigits: format,
|
||||||
|
maximumFractionDigits: format
|
||||||
|
});
|
||||||
|
else
|
||||||
|
formatter = new Intl.NumberFormat(this.locale);
|
||||||
|
|
||||||
this.numberFormats.set(format, formatter);
|
this.numberFormats.set(format, formatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,6 +265,29 @@ export default class TwitchData extends Module {
|
||||||
return get('data.user', data);
|
return get('data.user', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries Apollo for the user's current game, details given the user id or name. One of (id, login) MUST be specified
|
||||||
|
* @function getUserGame
|
||||||
|
* @memberof TwitchData
|
||||||
|
* @async
|
||||||
|
*
|
||||||
|
* @param {int|string|null|undefined} id - the user id number (can be an integer string)
|
||||||
|
* @param {string|null|undefined} login - the username
|
||||||
|
* @returns {Object} information about the requested user
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* console.log(this.twitch_data.getUserGame(19571641, null));
|
||||||
|
*/
|
||||||
|
async getUserGame(id, login) {
|
||||||
|
const data = await this.queryApollo(
|
||||||
|
await import(/* webpackChunkName: 'queries' */ './data/user-game.gql'),
|
||||||
|
{ id, login }
|
||||||
|
);
|
||||||
|
|
||||||
|
return get('data.user.broadcastSettings.game', data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for the logged in user's relationship to the channel with given the id or name. One of (id, login) MUST be specified
|
* Queries Apollo for the logged in user's relationship to the channel with given the id or name. One of (id, login) MUST be specified
|
||||||
* @function getUserSelf
|
* @function getUserSelf
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue