mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-07 14:50:56 +00:00
4.20.0
* Added: Emote Visibility Control for Emote Menu. You can now hide emotes you don't want from your emote menu. You still have them, and they'll still appear in chat if someone else uses them, but it helps keep the clutter down in your menu. (Closes #811) * Added: Setting to toggle mute for the player when middle-clicking it. (Closes #812) * Added: Setting to toggle the bold style applied to chat mentions. (Closes #816) * Fixed: No background color being applied to Highlight My Message chat messages when using the new Twitch layout. Now, the default Twitch purple will be used when FFZ hasn't yet extracted the color for the current channel. Extracting the channel color is still broken at this time. (Closes #821) * Fixed: The player volume resetting to 100% when changing channels. (Closes #820) * Fixed: Chat appearing incorrectly when a custom width smaller than 340 pixels is set. (Closes #819)
This commit is contained in:
parent
1c311c89bf
commit
8c9a3aa8a4
15 changed files with 525 additions and 290 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.19.13",
|
"version": "4.20.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": {
|
||||||
|
|
|
@ -194,6 +194,52 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Hidden Checking
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
toggleHidden(source, id, value = null) {
|
||||||
|
const key = `hidden-emotes.${source}`,
|
||||||
|
p = this.settings.provider,
|
||||||
|
hidden = p.get(key, []),
|
||||||
|
|
||||||
|
idx = hidden.indexOf(id);
|
||||||
|
|
||||||
|
if ( value === null )
|
||||||
|
value = idx === -1;
|
||||||
|
|
||||||
|
if ( value && idx === -1 )
|
||||||
|
hidden.push(id);
|
||||||
|
else if ( ! value && idx !== -1 )
|
||||||
|
hidden.splice(idx, 1);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( hidden.length )
|
||||||
|
p.set(key, hidden);
|
||||||
|
else
|
||||||
|
p.delete(key);
|
||||||
|
|
||||||
|
this.emit(':change-hidden', source, id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
isHidden(source, id) {
|
||||||
|
return this.getHidden(source).includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHidden(source) {
|
||||||
|
return this.settings.provider.get(`hidden-emotes.${source}`, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHidden(source, list) {
|
||||||
|
const key = `hidden-emotes.${source}`;
|
||||||
|
if ( ! Array.isArray(list) || ! list.length )
|
||||||
|
this.settings.provider.delete(key);
|
||||||
|
else
|
||||||
|
this.settings.provider.set(key, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Favorite Checking
|
// Favorite Checking
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
|
@ -628,6 +628,15 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.bold-mentions', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
component: 'setting-check-box',
|
||||||
|
path: 'Chat > Filtering >> Appearance',
|
||||||
|
title: 'Display mentions in chat with a bold font.'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.filtering.mention-color', {
|
this.settings.add('chat.filtering.mention-color', {
|
||||||
default: '',
|
default: '',
|
||||||
ui: {
|
ui: {
|
||||||
|
|
|
@ -97,6 +97,8 @@ export default class Channel extends Module {
|
||||||
|
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
this.updateChannelColor();
|
||||||
|
|
||||||
this.ChannelPage.on('mount', this.wrapChannelPage, this);
|
this.ChannelPage.on('mount', this.wrapChannelPage, this);
|
||||||
this.RaidController.on('mount', this.wrapRaidController, this);
|
this.RaidController.on('mount', this.wrapRaidController, this);
|
||||||
this.RaidController.on('update', this.noAutoRaids, this);
|
this.RaidController.on('update', this.noAutoRaids, this);
|
||||||
|
|
|
@ -253,6 +253,7 @@ export default class EmoteMenu extends Module {
|
||||||
this.on('chat.emotes:update-user-sets', this.maybeUpdate, this);
|
this.on('chat.emotes:update-user-sets', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:update-room-sets', this.maybeUpdate, this);
|
this.on('chat.emotes:update-room-sets', this.maybeUpdate, this);
|
||||||
this.on('chat.emotes:change-favorite', this.maybeUpdate, this);
|
this.on('chat.emotes:change-favorite', this.maybeUpdate, this);
|
||||||
|
this.on('chat.emotes:change-hidden', this.maybeUpdate, this);
|
||||||
this.on('chat.emoji:populated', this.maybeUpdate, this);
|
this.on('chat.emoji:populated', this.maybeUpdate, this);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emote-menu.enabled', () =>
|
this.chat.context.on('changed:chat.emote-menu.enabled', () =>
|
||||||
|
@ -491,9 +492,12 @@ export default class EmoteMenu extends Module {
|
||||||
this.props.startObserving(this.ref, this);
|
this.props.startObserving(this.ref, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const collapsed = storage.get('emote-menu.collapsed') || [];
|
const collapsed = storage.get('emote-menu.collapsed'),
|
||||||
|
hidden = storage.get('emote-menu.hidden-sets');
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
collapsed: props.data && collapsed.includes(props.data.key),
|
hidden: hidden && props.data && hidden.includes(props.data.hide_key || props.data.key),
|
||||||
|
collapsed: collapsed && props.data && collapsed.includes(props.data.key),
|
||||||
intersecting: window.IntersectionObserver ? false : true
|
intersecting: window.IntersectionObserver ? false : true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +521,29 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
clickEmote(event) {
|
clickEmote(event) {
|
||||||
|
if ( this.props.visibility_control ) {
|
||||||
|
const ds = event.currentTarget.dataset;
|
||||||
|
let source, id = ds.id;
|
||||||
|
|
||||||
|
if ( ds.provider === 'twitch' )
|
||||||
|
source = 'twitch';
|
||||||
|
else if ( ds.provider === 'ffz' ) {
|
||||||
|
const emote_set = t.emotes.emote_sets[ds.set],
|
||||||
|
emote = emote_set && emote_set.emotes[id];
|
||||||
|
|
||||||
|
if ( ! emote )
|
||||||
|
return;
|
||||||
|
|
||||||
|
source = emote_set.source || 'ffz';
|
||||||
|
id = emote.id;
|
||||||
|
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
|
||||||
|
t.emotes.toggleHidden(source, id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( t.emotes.handleClick(event) )
|
if ( t.emotes.handleClick(event) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -524,6 +551,26 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
clickHeading() {
|
clickHeading() {
|
||||||
|
if ( this.props.visibility_control ) {
|
||||||
|
const hidden = storage.get('emote-menu.hidden-sets') || [],
|
||||||
|
key = this.props.data.hide_key || this.props.data.key,
|
||||||
|
idx = hidden.indexOf(key);
|
||||||
|
|
||||||
|
if ( key === 'twitch-current-channel' )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( idx === -1 ) {
|
||||||
|
hidden.push(key);
|
||||||
|
this.setState({hidden: true});
|
||||||
|
} else {
|
||||||
|
hidden.splice(idx, 1);
|
||||||
|
this.setState({hidden: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.set('emote-menu.hidden-sets', hidden);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( this.props.filtered )
|
if ( this.props.filtered )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -555,7 +602,8 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const data = this.props.data,
|
const data = this.props.data,
|
||||||
filtered = this.props.filtered;
|
filtered = this.props.filtered,
|
||||||
|
visibility = this.props.visibility_control;
|
||||||
|
|
||||||
let show_heading = ! (data.is_favorites && ! t.chat.context.get('chat.emote-menu.combine-tabs')) && t.chat.context.get('chat.emote-menu.show-heading');
|
let show_heading = ! (data.is_favorites && ! t.chat.context.get('chat.emote-menu.combine-tabs')) && t.chat.context.get('chat.emote-menu.show-heading');
|
||||||
if ( show_heading === 2 )
|
if ( show_heading === 2 )
|
||||||
|
@ -563,7 +611,11 @@ export default class EmoteMenu extends Module {
|
||||||
else
|
else
|
||||||
show_heading = !! show_heading;
|
show_heading = !! show_heading;
|
||||||
|
|
||||||
const collapsed = show_heading && ! filtered && this.state.collapsed;
|
if ( visibility )
|
||||||
|
show_heading = true;
|
||||||
|
|
||||||
|
const hidden = visibility ? this.state.hidden : false,
|
||||||
|
collapsed = visibility ? hidden : (show_heading && ! filtered && this.state.collapsed);
|
||||||
|
|
||||||
if ( ! data )
|
if ( ! data )
|
||||||
return null;
|
return null;
|
||||||
|
@ -613,15 +665,20 @@ export default class EmoteMenu extends Module {
|
||||||
{image}
|
{image}
|
||||||
<div class="tw-pd-l-05">
|
<div class="tw-pd-l-05">
|
||||||
{(data.i18n ? t.i18n.t(data.i18n, data.title) : data.title) || t.i18n.t('emote-menu.unknown', 'Unknown Source')}
|
{(data.i18n ? t.i18n.t(data.i18n, data.title) : data.title) || t.i18n.t('emote-menu.unknown', 'Unknown Source')}
|
||||||
{calendar && (<span
|
{! visibility && calendar && (<span
|
||||||
class={`tw-mg-x-05 ffz--expiry-info ffz-tooltip ffz-i-${calendar.icon}`}
|
class={`tw-mg-x-05 ffz--expiry-info ffz-tooltip ffz-i-${calendar.icon}`}
|
||||||
data-tooltip-type="html"
|
data-tooltip-type="html"
|
||||||
data-title={calendar.message}
|
data-title={calendar.message}
|
||||||
/>)}
|
/>)}
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-flex-grow-1" />
|
<div class="tw-flex-grow-1" />
|
||||||
{source}
|
{visibility ?
|
||||||
{filtered ? '' : <figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />}
|
(hidden ?
|
||||||
|
t.i18n.t('emote-menu.visibility.hidden', 'Hidden') :
|
||||||
|
t.i18n.t('emote-menu.visibility.visible', 'Visible') )
|
||||||
|
: source
|
||||||
|
}
|
||||||
|
{(visibility ? false : filtered) ? '' : <figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />}
|
||||||
</heading>) : null}
|
</heading>) : null}
|
||||||
{collapsed || this.renderBody(show_heading)}
|
{collapsed || this.renderBody(show_heading)}
|
||||||
</section>)
|
</section>)
|
||||||
|
@ -659,7 +716,7 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
return (<div class="tw-pd-1 tw-border-b tw-c-background-alt tw-align-center">
|
return (<div class="tw-pd-1 tw-border-b tw-c-background-alt tw-align-center">
|
||||||
{emotes}
|
{emotes}
|
||||||
{!filtered && this.renderSellout()}
|
{! this.props.visibility_control && !filtered && this.renderSellout()}
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,9 +724,12 @@ export default class EmoteMenu extends Module {
|
||||||
if ( ! this.state.intersecting )
|
if ( ! this.state.intersecting )
|
||||||
return <span key={emote.id} class="emote-picker__placeholder" style={{width: `${emote.width||28}px`, height: `${emote.height||28}px`}} />;
|
return <span key={emote.id} class="emote-picker__placeholder" style={{width: `${emote.width||28}px`, height: `${emote.height||28}px`}} />;
|
||||||
|
|
||||||
|
const visibility = this.props.visibility_control,
|
||||||
|
hidden = visibility && emote.hidden;
|
||||||
|
|
||||||
return (<button
|
return (<button
|
||||||
key={emote.id}
|
key={emote.id}
|
||||||
class={`ffz-tooltip emote-picker__emote-link${locked ? ' locked' : ''}`}
|
class={`ffz-tooltip emote-picker__emote-link${!visibility && locked ? ' locked' : ''}${hidden ? ' hidden' : ''}`}
|
||||||
data-tooltip-type="emote"
|
data-tooltip-type="emote"
|
||||||
data-provider={emote.provider}
|
data-provider={emote.provider}
|
||||||
data-id={emote.id}
|
data-id={emote.id}
|
||||||
|
@ -681,7 +741,7 @@ export default class EmoteMenu extends Module {
|
||||||
aria-label={emote.name}
|
aria-label={emote.name}
|
||||||
data-locked={emote.locked}
|
data-locked={emote.locked}
|
||||||
data-sellout={sellout}
|
data-sellout={sellout}
|
||||||
onClick={!emote.locked && this.clickEmote}
|
onClick={(this.props.visibility_control || !emote.locked) && this.clickEmote}
|
||||||
>
|
>
|
||||||
<figure class="emote-picker__emote-figure">
|
<figure class="emote-picker__emote-figure">
|
||||||
<img
|
<img
|
||||||
|
@ -693,8 +753,9 @@ export default class EmoteMenu extends Module {
|
||||||
width={emote.width ? `${emote.width}px` : null}
|
width={emote.width ? `${emote.width}px` : null}
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
{emote.favorite && <figure class="ffz--favorite ffz-i-star" />}
|
{! visibility && emote.favorite && <figure class="ffz--favorite ffz-i-star" />}
|
||||||
{locked && <figure class="ffz-i-lock" />}
|
{! visibility && locked && <figure class="ffz-i-lock" />}
|
||||||
|
{hidden && <figure class="ffz-i-eye-off" />}
|
||||||
</button>)
|
</button>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,6 +937,7 @@ export default class EmoteMenu extends Module {
|
||||||
//this.clickRefresh = this.clickRefresh.bind(this);
|
//this.clickRefresh = this.clickRefresh.bind(this);
|
||||||
this.handleFilterChange = this.handleFilterChange.bind(this);
|
this.handleFilterChange = this.handleFilterChange.bind(this);
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
|
this.toggleVisibilityControl = this.toggleVisibilityControl.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
createObserver() {
|
createObserver() {
|
||||||
|
@ -1079,6 +1141,10 @@ export default class EmoteMenu extends Module {
|
||||||
});
|
});
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
toggleVisibilityControl() {
|
||||||
|
this.setState(this.filterState(this.state.filter, this.state, ! this.state.visibility_control));
|
||||||
|
}
|
||||||
|
|
||||||
handleFilterChange(event) {
|
handleFilterChange(event) {
|
||||||
this.setState(this.filterState(event.target.value, this.state));
|
this.setState(this.filterState(event.target.value, this.state));
|
||||||
}
|
}
|
||||||
|
@ -1115,30 +1181,43 @@ export default class EmoteMenu extends Module {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
filterState(input, old_state) {
|
filterState(input, old_state, visibility_control) {
|
||||||
const state = Object.assign({}, old_state);
|
const state = Object.assign({}, old_state);
|
||||||
|
|
||||||
|
if ( visibility_control != null )
|
||||||
|
state.visibility_control = visibility_control;
|
||||||
|
else
|
||||||
|
visibility_control = state.visibility_control;
|
||||||
|
|
||||||
state.filter = input;
|
state.filter = input;
|
||||||
state.filtered = input && input.length > 0 && input !== ':' || false;
|
state.filtered = input && input.length > 0 && input !== ':' || false;
|
||||||
|
|
||||||
state.filtered_channel_sets = this.filterSets(input, state.channel_sets);
|
state.filtered_channel_sets = this.filterSets(input, state.channel_sets, visibility_control);
|
||||||
state.filtered_all_sets = this.filterSets(input, state.all_sets);
|
state.filtered_all_sets = this.filterSets(input, state.all_sets, visibility_control);
|
||||||
state.filtered_fav_sets = this.filterSets(input, state.fav_sets);
|
state.filtered_fav_sets = this.filterSets(input, state.fav_sets, visibility_control);
|
||||||
state.filtered_emoji_sets = this.filterSets(input, state.emoji_sets);
|
state.filtered_emoji_sets = this.filterSets(input, state.emoji_sets, visibility_control);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
filterSets(input, sets) {
|
filterSets(input, sets, visibility_control) {
|
||||||
const out = [];
|
const out = [];
|
||||||
if ( ! sets || ! sets.length )
|
if ( ! sets || ! sets.length )
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
const filtering = input && input.length > 0 && input !== ':';
|
const filtering = input && input.length > 0 && input !== ':',
|
||||||
|
hidden_sets = storage.get('emote-menu.hidden-sets', []);
|
||||||
|
|
||||||
for(const emote_set of sets) {
|
for(const emote_set of sets) {
|
||||||
const filtered = emote_set.filtered_emotes = emote_set.emotes.filter(emote =>
|
if ( ! visibility_control && hidden_sets.includes(emote_set.key) )
|
||||||
! filtering || (! emote.locked && this.doesEmoteMatch(input, emote)));
|
continue;
|
||||||
|
|
||||||
|
const filtered = emote_set.filtered_emotes = emote_set.emotes.filter(emote => {
|
||||||
|
if ( ! visibility_control && emote.hidden )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ! filtering || (! emote.locked && this.doesEmoteMatch(input, emote))
|
||||||
|
});
|
||||||
|
|
||||||
if ( filtered.length )
|
if ( filtered.length )
|
||||||
out.push(emote_set);
|
out.push(emote_set);
|
||||||
|
@ -1206,6 +1285,7 @@ export default class EmoteMenu extends Module {
|
||||||
image: t.emoji.getFullImage(source.image),
|
image: t.emoji.getFullImage(source.image),
|
||||||
i18n: `emoji.category.${emoji.category.toSnakeCase()}`,
|
i18n: `emoji.category.${emoji.category.toSnakeCase()}`,
|
||||||
title: emoji.category,
|
title: emoji.category,
|
||||||
|
src: 'emoji',
|
||||||
source: 'Emoji',
|
source: 'Emoji',
|
||||||
source_i18n: 'emote-menu.emoji',
|
source_i18n: 'emote-menu.emoji',
|
||||||
emotes: cat
|
emotes: cat
|
||||||
|
@ -1331,6 +1411,7 @@ export default class EmoteMenu extends Module {
|
||||||
const emote_sets = props.emote_data && props.emote_data.emoteSets,
|
const emote_sets = props.emote_data && props.emote_data.emoteSets,
|
||||||
emote_map = props.emote_data && props.emote_data.emoteMap,
|
emote_map = props.emote_data && props.emote_data.emoteMap,
|
||||||
twitch_favorites = t.emotes.getFavorites('twitch'),
|
twitch_favorites = t.emotes.getFavorites('twitch'),
|
||||||
|
twitch_hidden = t.emotes.getHidden('twitch'),
|
||||||
twitch_seen = new Set,
|
twitch_seen = new Set,
|
||||||
|
|
||||||
bits_unlocked = [],
|
bits_unlocked = [],
|
||||||
|
@ -1507,6 +1588,7 @@ export default class EmoteMenu extends Module {
|
||||||
overridden: overridden ? mapped.id : null,
|
overridden: overridden ? mapped.id : null,
|
||||||
misc: ! chan,
|
misc: ! chan,
|
||||||
bits: is_bits,
|
bits: is_bits,
|
||||||
|
hidden: twitch_hidden.includes(id),
|
||||||
favorite: is_fav
|
favorite: is_fav
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1546,6 +1628,7 @@ export default class EmoteMenu extends Module {
|
||||||
section = {
|
section = {
|
||||||
sort_key: -10,
|
sort_key: -10,
|
||||||
key: `twitch-current-channel`,
|
key: `twitch-current-channel`,
|
||||||
|
hide_key: `twitch-${user.id}`,
|
||||||
image: badge && badge.image1x,
|
image: badge && badge.image1x,
|
||||||
image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
|
image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
|
||||||
icon: 'twitch',
|
icon: 'twitch',
|
||||||
|
@ -1609,7 +1692,8 @@ export default class EmoteMenu extends Module {
|
||||||
locked: locked && ! seen,
|
locked: locked && ! seen,
|
||||||
src: `${base}/1.0`,
|
src: `${base}/1.0`,
|
||||||
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
||||||
favorite: is_fav
|
favorite: is_fav,
|
||||||
|
hidden: twitch_hidden.includes(id)
|
||||||
};
|
};
|
||||||
|
|
||||||
emotes.push(em);
|
emotes.push(em);
|
||||||
|
@ -1663,7 +1747,8 @@ export default class EmoteMenu extends Module {
|
||||||
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
||||||
bits: true,
|
bits: true,
|
||||||
bit_value: summary.threshold,
|
bit_value: summary.threshold,
|
||||||
favorite: is_fav
|
favorite: is_fav,
|
||||||
|
hidden: twitch_hidden.includes(id)
|
||||||
};
|
};
|
||||||
|
|
||||||
emotes.push(em);
|
emotes.push(em);
|
||||||
|
@ -1744,6 +1829,7 @@ export default class EmoteMenu extends Module {
|
||||||
|
|
||||||
const fav_key = emote_set.source || 'ffz',
|
const fav_key = emote_set.source || 'ffz',
|
||||||
known_favs = t.emotes.getFavorites(fav_key),
|
known_favs = t.emotes.getFavorites(fav_key),
|
||||||
|
known_hidden = t.emotes.getHidden(fav_key),
|
||||||
seen_favs = seen_favorites[fav_key] = seen_favorites[fav_key] || new Set;
|
seen_favs = seen_favorites[fav_key] = seen_favorites[fav_key] || new Set;
|
||||||
|
|
||||||
const key = `${emote_set.merge_source || fav_key}-${emote_set.merge_id || emote_set.id}`,
|
const key = `${emote_set.merge_source || fav_key}-${emote_set.merge_id || emote_set.id}`,
|
||||||
|
@ -1802,6 +1888,7 @@ export default class EmoteMenu extends Module {
|
||||||
srcSet: emote.srcSet,
|
srcSet: emote.srcSet,
|
||||||
name: emote.name,
|
name: emote.name,
|
||||||
favorite: is_fav,
|
favorite: is_fav,
|
||||||
|
hidden: known_hidden.includes(emote.id),
|
||||||
height: emote.height,
|
height: emote.height,
|
||||||
width: emote.width
|
width: emote.width
|
||||||
};
|
};
|
||||||
|
@ -1921,6 +2008,8 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const visibility = this.state.visibility_control;
|
||||||
|
|
||||||
return (<div
|
return (<div
|
||||||
class={`tw-balloon tw-balloon--md tw-balloon--up tw-balloon--right tw-block tw-absolute ffz--emote-picker${padding ? ' reduced-padding' : ''}`}
|
class={`tw-balloon tw-balloon--md tw-balloon--up tw-balloon--right tw-block tw-absolute ffz--emote-picker${padding ? ' reduced-padding' : ''}`}
|
||||||
data-a-target="emote-picker"
|
data-a-target="emote-picker"
|
||||||
|
@ -1934,12 +2023,13 @@ export default class EmoteMenu extends Module {
|
||||||
<div ref={this.saveScrollRef} class="simplebar-scroll-content">
|
<div ref={this.saveScrollRef} class="simplebar-scroll-content">
|
||||||
<div class="simplebar-content">
|
<div class="simplebar-content">
|
||||||
{loading && this.renderLoading()}
|
{loading && this.renderLoading()}
|
||||||
{!loading && sets && sets.map(data => data && createElement(
|
{!loading && sets && sets.map(data => data && (! visibility || (! data.emoji && ! data.is_favorites)) && createElement(
|
||||||
data.emoji ? t.EmojiSection : t.MenuSection,
|
data.emoji ? t.EmojiSection : t.MenuSection,
|
||||||
{
|
{
|
||||||
key: data.key,
|
key: data.key,
|
||||||
data,
|
data,
|
||||||
filtered: this.state.filtered,
|
filtered: this.state.filtered,
|
||||||
|
visibility_control: visibility,
|
||||||
onClickToken: this.props.onClickToken,
|
onClickToken: this.props.onClickToken,
|
||||||
startObserving: this.startObserving,
|
startObserving: this.startObserving,
|
||||||
stopObserving: this.stopObserving
|
stopObserving: this.stopObserving
|
||||||
|
@ -1967,7 +2057,26 @@ export default class EmoteMenu extends Module {
|
||||||
onChange={this.handleFilterChange}
|
onChange={this.handleFilterChange}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
{(no_tabs || is_emoji) && this.state.has_emoji_tab && <t.EmojiTonePicker
|
{(no_tabs || ! is_emoji) && <div class="tw-relative tw-tooltip-wrapper tw-mg-l-1">
|
||||||
|
<button
|
||||||
|
class={`tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon--primary tw-core-button tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative${this.state.visibility_control ? ' tw-core-button--primary' : ' tw-button-icon'}`}
|
||||||
|
onClick={this.toggleVisibilityControl}
|
||||||
|
>
|
||||||
|
<span class="tw-button-icon__icon tw-mg-x-05">
|
||||||
|
<figure class={this.state.visibility_control ? 'ffz-i-eye-off' : 'ffz-i-eye'} />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="tw-tooltip tw-tooltip--up tw-tooltip--align-right">
|
||||||
|
{this.state.visibility_control ?
|
||||||
|
t.i18n.t('emote-menu.toggle-hide.on', 'Exit Emote Visibility Control') :
|
||||||
|
t.i18n.t('emote-menu.toggle-hide.off', 'Emote Visibility Control')
|
||||||
|
}
|
||||||
|
<div class="tw-mg-t-1 ffz--tooltip-explain">
|
||||||
|
{t.i18n.t('emote-menu.toggle-hide.info', 'Emote Visibility Control allows you to hide emotes from your emote menu, either individually or by set. With Emote Visibility Control enabled, just click an emote to hide or unhide it. Please note that you will still see the emotes in chat if someone uses them, but they won\'t appear in your emote menu.')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>}
|
||||||
|
{(no_tabs || is_emoji) && ! visibility && this.state.has_emoji_tab && <t.EmojiTonePicker
|
||||||
tone={this.state.tone}
|
tone={this.state.tone}
|
||||||
choices={this.state.tone_emoji}
|
choices={this.state.tone_emoji}
|
||||||
pickTone={this.pickTone}
|
pickTone={this.pickTone}
|
||||||
|
@ -1975,7 +2084,7 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>)}
|
||||||
<div class="emote-picker__tab-nav-container tw-flex tw-border-t tw-c-background-alt">
|
<div class="emote-picker__tab-nav-container tw-flex tw-border-t tw-c-background-alt">
|
||||||
<div class={`emote-picker-tab-item${tab === 'fav' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
{! visibility && <div class={`emote-picker-tab-item${tab === 'fav' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
||||||
<button
|
<button
|
||||||
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'fav' ? ' tw-interactable--selected' : ''}`}
|
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'fav' ? ' tw-interactable--selected' : ''}`}
|
||||||
id="emote-picker__fav"
|
id="emote-picker__fav"
|
||||||
|
@ -1988,7 +2097,7 @@ export default class EmoteMenu extends Module {
|
||||||
<figure class="ffz-i-star" />
|
<figure class="ffz-i-star" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>}
|
||||||
{this.state.has_channel_tab && <div class={`emote-picker-tab-item${tab === 'channel' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
{this.state.has_channel_tab && <div class={`emote-picker-tab-item${tab === 'channel' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
||||||
<button
|
<button
|
||||||
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'channel' ? ' tw-interactable--selected' : ''}`}
|
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'channel' ? ' tw-interactable--selected' : ''}`}
|
||||||
|
@ -2017,7 +2126,7 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{this.state.has_emoji_tab && <div class={`emote-picker-tab-item${tab === 'emoji' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
{! visibility && this.state.has_emoji_tab && <div class={`emote-picker-tab-item${tab === 'emoji' ? ' emote-picker-tab-item--active' : ''} tw-relative`}>
|
||||||
<button
|
<button
|
||||||
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'emoji' ? ' tw-interactable--selected' : ''}`}
|
class={`ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive${tab === 'emoji' ? ' tw-interactable--selected' : ''}`}
|
||||||
id="emote-picker__emoji"
|
id="emote-picker__emoji"
|
||||||
|
|
|
@ -677,6 +677,7 @@ export default class ChatHook extends Module {
|
||||||
this.chat.context.on('changed:chat.filtering.display-deleted', this.updateChatLines, this);
|
this.chat.context.on('changed:chat.filtering.display-deleted', this.updateChatLines, this);
|
||||||
this.chat.context.on('changed:chat.filtering.display-mod-action', this.updateChatLines, this);
|
this.chat.context.on('changed:chat.filtering.display-mod-action', this.updateChatLines, this);
|
||||||
this.chat.context.on('changed:chat.filtering.clickable-mentions', val => this.css_tweaks.toggle('clickable-mentions', val));
|
this.chat.context.on('changed:chat.filtering.clickable-mentions', val => this.css_tweaks.toggle('clickable-mentions', val));
|
||||||
|
this.chat.context.on('changed:chat.filtering.bold-mentions', val => this.css_tweaks.toggle('chat-mention-no-bold', ! val));
|
||||||
this.chat.context.on('changed:chat.pin-resubs', val => {
|
this.chat.context.on('changed:chat.pin-resubs', val => {
|
||||||
if ( val ) {
|
if ( val ) {
|
||||||
this.updateInlineCallouts();
|
this.updateInlineCallouts();
|
||||||
|
@ -713,6 +714,7 @@ export default class ChatHook extends Module {
|
||||||
this.css_tweaks.toggle('chat-deleted-fade', val < 2);
|
this.css_tweaks.toggle('chat-deleted-fade', val < 2);
|
||||||
|
|
||||||
this.css_tweaks.toggle('clickable-mentions', this.chat.context.get('chat.filtering.clickable-mentions'));
|
this.css_tweaks.toggle('clickable-mentions', this.chat.context.get('chat.filtering.clickable-mentions'));
|
||||||
|
this.css_tweaks.toggle('chat-mention-no-bold', ! this.chat.context.get('chat.filtering.bold-mentions'));
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.hide-community-highlights', val => this.css_tweaks.toggleHide('community-highlights', val));
|
this.chat.context.on('changed:chat.hide-community-highlights', val => this.css_tweaks.toggleHide('community-highlights', val));
|
||||||
|
|
||||||
|
|
|
@ -482,12 +482,13 @@ export default class Input extends Module {
|
||||||
startingResults = [],
|
startingResults = [],
|
||||||
otherResults = [],
|
otherResults = [],
|
||||||
favorites = this.emotes.getFavorites('twitch'),
|
favorites = this.emotes.getFavorites('twitch'),
|
||||||
|
hidden = this.emotes.getHidden('twitch'),
|
||||||
search = input.startsWith(':') ? input.slice(1) : input;
|
search = input.startsWith(':') ? input.slice(1) : input;
|
||||||
|
|
||||||
for (const set of hydratedEmotes) {
|
for (const set of hydratedEmotes) {
|
||||||
if (set && Array.isArray(set.emotes)) {
|
if (set && Array.isArray(set.emotes)) {
|
||||||
for (const emote of set.emotes) {
|
for (const emote of set.emotes) {
|
||||||
if (inst.doesEmoteMatchTerm(emote, search)) {
|
if (inst.doesEmoteMatchTerm(emote, search) && ! hidden.includes(emote.id)) {
|
||||||
const favorite = favorites.includes(emote.id);
|
const favorite = favorites.includes(emote.id);
|
||||||
const element = {
|
const element = {
|
||||||
current: input,
|
current: input,
|
||||||
|
@ -590,7 +591,7 @@ export default class Input extends Module {
|
||||||
for(const set of sets) {
|
for(const set of sets) {
|
||||||
if ( set && set.emotes )
|
if ( set && set.emotes )
|
||||||
for(const emote of Object.values(set.emotes))
|
for(const emote of Object.values(set.emotes))
|
||||||
if ( inst.doesEmoteMatchTerm(emote, search) && !added_emotes.has(emote.name) ) {
|
if ( inst.doesEmoteMatchTerm(emote, search) && !added_emotes.has(emote.name) && ! this.emotes.isHidden(set.source || 'ffz', emote.id) ) {
|
||||||
const favorite = this.emotes.isFavorite(set.source || 'ffz', emote.id);
|
const favorite = this.emotes.isFavorite(set.source || 'ffz', emote.id);
|
||||||
results.push({
|
results.push({
|
||||||
current: input,
|
current: input,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
strong.chat-line__message-mention {
|
||||||
|
font-weight: 100 !important;
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
.chat-shell__expanded {
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
body .whispers--theatre-mode.whispers--right-column-expanded-beside {
|
body .whispers--theatre-mode.whispers--right-column-expanded-beside {
|
||||||
right: var(--ffz-chat-width);
|
right: var(--ffz-chat-width);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {createElement, on, off} from 'utilities/dom';
|
import {createElement, on, off} from 'utilities/dom';
|
||||||
|
import {debounce} from 'utilities/object';
|
||||||
|
|
||||||
export const PLAYER_ROUTES = [
|
export const PLAYER_ROUTES = [
|
||||||
'front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos',
|
'front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos',
|
||||||
|
@ -231,6 +232,15 @@ export default class Player extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('player.mute-click', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Player > General >> Volume',
|
||||||
|
title: 'Mute or unmute the player by middle-clicking.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('player.volume-scroll', {
|
this.settings.add('player.volume-scroll', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -559,6 +569,9 @@ export default class Player extends Module {
|
||||||
|
|
||||||
this._ffz_installed = true;
|
this._ffz_installed = true;
|
||||||
|
|
||||||
|
if ( ! this._ffzUpdateVolume )
|
||||||
|
this._ffzUpdateVolume = debounce(this.ffzUpdateVolume.bind(this));
|
||||||
|
|
||||||
if ( ! this._ffzUpdateState )
|
if ( ! this._ffzUpdateState )
|
||||||
this._ffzUpdateState = this.ffzUpdateState.bind(this);
|
this._ffzUpdateState = this.ffzUpdateState.bind(this);
|
||||||
|
|
||||||
|
@ -607,6 +620,16 @@ export default class Player extends Module {
|
||||||
this.ffzStopAutoplay();
|
this.ffzStopAutoplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzUpdateVolume = function() {
|
||||||
|
const player = this.props.mediaPlayerInstance,
|
||||||
|
video = player?.mediaSinkManager?.video || player?.core?.mediaSinkManager?.video;
|
||||||
|
if ( video ) {
|
||||||
|
const volume = video.volume;
|
||||||
|
if ( ! player.isMuted() && ! video.muted && player.getVolume() !== volume )
|
||||||
|
player.setVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cls.prototype.ffzUninstall = function() {
|
cls.prototype.ffzUninstall = function() {
|
||||||
if ( this._ffz_state_raf )
|
if ( this._ffz_state_raf )
|
||||||
cancelAnimationFrame(this._ffz_state_raf);
|
cancelAnimationFrame(this._ffz_state_raf);
|
||||||
|
@ -698,7 +721,12 @@ export default class Player extends Module {
|
||||||
this._ffz_listeners = true;
|
this._ffz_listeners = true;
|
||||||
if ( ! this._ffz_scroll_handler )
|
if ( ! this._ffz_scroll_handler )
|
||||||
this._ffz_scroll_handler = this.ffzScrollHandler.bind(this);
|
this._ffz_scroll_handler = this.ffzScrollHandler.bind(this);
|
||||||
|
|
||||||
|
if ( ! this._ffz_click_handler )
|
||||||
|
this._ffz_click_handler = this.ffzClickHandler.bind(this);
|
||||||
|
|
||||||
on(cont, 'wheel', this._ffz_scroll_handler);
|
on(cont, 'wheel', this._ffz_scroll_handler);
|
||||||
|
on(cont, 'mousedown', this._ffz_click_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.prototype.ffzRemoveListeners = function() {
|
cls.prototype.ffzRemoveListeners = function() {
|
||||||
|
@ -711,9 +739,27 @@ export default class Player extends Module {
|
||||||
this._ffz_scroll_handler = null;
|
this._ffz_scroll_handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this._ffz_click_handler ) {
|
||||||
|
off(cont, 'mousedown', this._ffz_click_handler);
|
||||||
|
this._ffz_click_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
this._ffz_listeners = false;
|
this._ffz_listeners = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzClickHandler = function(event) {
|
||||||
|
if ( ! t.settings.get('player.mute-click') || ! event || event.button !== 1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const player = this.props?.mediaPlayerInstance;
|
||||||
|
if ( ! player?.isMuted )
|
||||||
|
return;
|
||||||
|
|
||||||
|
player.setMuted(! player.isMuted());
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
cls.prototype.ffzScrollHandler = function(event) {
|
cls.prototype.ffzScrollHandler = function(event) {
|
||||||
if ( ! t.settings.get('player.volume-scroll') )
|
if ( ! t.settings.get('player.volume-scroll') )
|
||||||
return;
|
return;
|
||||||
|
@ -956,10 +1002,11 @@ export default class Player extends Module {
|
||||||
this.addCompressorButton(inst, false);
|
this.addCompressorButton(inst, false);
|
||||||
|
|
||||||
const player = inst?.props?.mediaPlayerInstance;
|
const player = inst?.props?.mediaPlayerInstance;
|
||||||
if ( player && ! this.settings.get('player.allow-catchup') ) {
|
if ( player && ! this.settings.get('player.allow-catchup') && player.setLiveSpeedUpRate )
|
||||||
if ( player.setLiveSpeedUpRate )
|
player.setLiveSpeedUpRate(1);
|
||||||
player.setLiveSpeedUpRate(1);
|
|
||||||
}
|
if ( inst._ffzUpdateVolume )
|
||||||
|
inst._ffzUpdateVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1003,7 +1050,7 @@ export default class Player extends Module {
|
||||||
<div class="tw-tooltip tw-tooltip--align-left tw-tooltip--up" role="tooltip">
|
<div class="tw-tooltip tw-tooltip--align-left tw-tooltip--up" role="tooltip">
|
||||||
<div>
|
<div>
|
||||||
{tip = (<div class="ffz--p-tip" />)}
|
{tip = (<div class="ffz--p-tip" />)}
|
||||||
{extra = (<div class="ffz--p-extra tw-pd-t-05" />)}
|
{extra = (<div class="ffz--p-extra tw-pd-t-05 ffz--tooltip-explain" />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
@ -1199,7 +1246,7 @@ export default class Player extends Module {
|
||||||
|
|
||||||
let thing = container.querySelector('button[data-a-target="player-theatre-mode-button"]');
|
let thing = container.querySelector('button[data-a-target="player-theatre-mode-button"]');
|
||||||
if ( ! thing )
|
if ( ! thing )
|
||||||
thing = container.querySelector('button[data-a-target="player-fullscreen-button"]');
|
thing = container.querySelector('button[data-a-target="player-fullscreen-button"]');
|
||||||
|
|
||||||
if ( thing ) {
|
if ( thing ) {
|
||||||
container.insertBefore(cont, thing.parentElement);
|
container.insertBefore(cont, thing.parentElement);
|
||||||
|
@ -1416,7 +1463,7 @@ export default class Player extends Module {
|
||||||
const video = player.mediaSinkManager?.video || player.core?.mediaSinkManager?.video;
|
const video = player.mediaSinkManager?.video || player.core?.mediaSinkManager?.video;
|
||||||
if ( video?._ffz_compressor && player.attachHTMLVideoElement ) {
|
if ( video?._ffz_compressor && player.attachHTMLVideoElement ) {
|
||||||
const new_vid = createElement('video'),
|
const new_vid = createElement('video'),
|
||||||
vol = player.getVolume(),
|
vol = video?.volume ?? player.getVolume(),
|
||||||
muted = player.isMuted();
|
muted = player.isMuted();
|
||||||
new_vid.volume = muted ? 0 : vol;
|
new_vid.volume = muted ? 0 : vol;
|
||||||
new_vid.playsInline = true;
|
new_vid.playsInline = true;
|
||||||
|
|
|
@ -305,14 +305,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.locked {
|
&.locked,
|
||||||
cursor: not-allowed;
|
&.hidden {
|
||||||
|
|
||||||
img {
|
img {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-i-lock {
|
.ffz-i-lock,
|
||||||
|
.ffz-i-eye-off {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0; right: 0;
|
bottom: 0; right: 0;
|
||||||
border-radius: .2rem;
|
border-radius: .2rem;
|
||||||
|
@ -320,7 +320,10 @@
|
||||||
background-color: rgba(0,0,0,0.5);
|
background-color: rgba(0,0,0,0.5);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.locked {
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
src/utilities/data/user-color.gql
Normal file
6
src/utilities/data/user-color.gql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
query FFZ_UserColor($id: ID, $login: String) {
|
||||||
|
user(id: $id, login: $login) {
|
||||||
|
id
|
||||||
|
primaryColorHex
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ query FFZ_FetchUser($id: ID, $login: String) {
|
||||||
login
|
login
|
||||||
displayName
|
displayName
|
||||||
profileImageURL(width: 50)
|
profileImageURL(width: 50)
|
||||||
|
primaryColorHex
|
||||||
roles {
|
roles {
|
||||||
isAffiliate
|
isAffiliate
|
||||||
isPartner
|
isPartner
|
||||||
|
|
|
@ -52,7 +52,6 @@ const ALGOLIA_LANGUAGES = {
|
||||||
*
|
*
|
||||||
* console.log(getAlgoliaLanguage('en'));
|
* console.log(getAlgoliaLanguage('en'));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getAlgoliaLanguage(locale) {
|
function getAlgoliaLanguage(locale) {
|
||||||
if ( ! locale )
|
if ( ! locale )
|
||||||
return ALGOLIA_LANGUAGES.en;
|
return ALGOLIA_LANGUAGES.en;
|
||||||
|
@ -65,12 +64,12 @@ function getAlgoliaLanguage(locale) {
|
||||||
return ALGOLIA_LANGUAGES[locale] || ALGOLIA_LANGUAGES.en;
|
return ALGOLIA_LANGUAGES[locale] || ALGOLIA_LANGUAGES.en;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TwitchData is a container for getting different types of Twitch data
|
* TwitchData is a container for getting different types of Twitch data
|
||||||
* @class TwitchData
|
* @class TwitchData
|
||||||
* @extends Module
|
* @extends Module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class TwitchData extends Module {
|
export default class TwitchData extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -163,20 +162,19 @@ export default class TwitchData extends Module {
|
||||||
// Categories
|
// Categories
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for categories matching the search argument
|
* Queries Apollo for categories matching the search argument
|
||||||
* @function getMatchingCategories
|
* @function getMatchingCategories
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {string} query - query text to match to a category
|
* @param {string} query - query text to match to a category
|
||||||
* @returns {Object} a collection of matches for the query string
|
* @returns {Object} a collection of matches for the query string
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getMatchingCategories("siege"));
|
* console.log(this.twitch_data.getMatchingCategories("siege"));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getMatchingCategories(query) {
|
async getMatchingCategories(query) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/search-category.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/search-category.gql'),
|
||||||
|
@ -190,21 +188,20 @@ export default class TwitchData extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for category details given the id or name. One of (id, name) MUST be specified
|
* Queries Apollo for category details given the id or name. One of (id, name) MUST be specified
|
||||||
* @function getCategory
|
* @function getCategory
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} id - the category id number (can be an integer string)
|
* @param {int|string|null|undefined} id - the category id number (can be an integer string)
|
||||||
* @param {string|null|undefined} name - the category name
|
* @param {string|null|undefined} name - the category name
|
||||||
* @returns {Object} information about the requested stream
|
* @returns {Object} information about the requested stream
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getCategory(null, 'Just Chatting'));
|
* console.log(this.twitch_data.getCategory(null, 'Just Chatting'));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getCategory(id, name) {
|
async getCategory(id, name) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/category-fetch.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/category-fetch.gql'),
|
||||||
|
@ -219,20 +216,19 @@ export default class TwitchData extends Module {
|
||||||
// Users
|
// Users
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for users matching the search argument
|
* Queries Apollo for users matching the search argument
|
||||||
* @function getMatchingUsers
|
* @function getMatchingUsers
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {string} query - query text to match to a username
|
* @param {string} query - query text to match to a username
|
||||||
* @returns {Object} a collection of matches for the query string
|
* @returns {Object} a collection of matches for the query string
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getMatchingUsers("ninja"));
|
* console.log(this.twitch_data.getMatchingUsers("ninja"));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getMatchingUsers(query) {
|
async getMatchingUsers(query) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/search-user.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/search-user.gql'),
|
||||||
|
@ -246,21 +242,20 @@ export default class TwitchData extends Module {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for user details given the id or name. One of (id, login) MUST be specified
|
* Queries Apollo for user details given the id or name. One of (id, login) MUST be specified
|
||||||
* @function getUser
|
* @function getUser
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} id - the user id number (can be an integer string)
|
* @param {int|string|null|undefined} id - the user id number (can be an integer string)
|
||||||
* @param {string|null|undefined} login - the username
|
* @param {string|null|undefined} login - the username
|
||||||
* @returns {Object} information about the requested user
|
* @returns {Object} information about the requested user
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getUser(19571641, null));
|
* console.log(this.twitch_data.getUser(19571641, null));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getUser(id, login) {
|
async getUser(id, login) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/user-fetch.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/user-fetch.gql'),
|
||||||
|
@ -270,21 +265,20 @@ export default class TwitchData extends Module {
|
||||||
return get('data.user', data);
|
return get('data.user', 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
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
||||||
* @param {string|null|undefined} login - the channel username
|
* @param {string|null|undefined} login - the channel username
|
||||||
* @returns {Object} information about your status in the channel
|
* @returns {Object} information about your status in the channel
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getUserSelf(null, "ninja"));
|
* console.log(this.twitch_data.getUserSelf(null, "ninja"));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getUserSelf(id, login) {
|
async getUserSelf(id, login) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/user-self.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/user-self.gql'),
|
||||||
|
@ -294,21 +288,20 @@ export default class TwitchData extends Module {
|
||||||
return get('data.user.self', data);
|
return get('data.user.self', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for the requested user's latest broadcast. One of (id, login) MUST be specified
|
* Queries Apollo for the requested user's latest broadcast. One of (id, login) MUST be specified
|
||||||
* @function getLastBroadcast
|
* @function getLastBroadcast
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
||||||
* @param {string|null|undefined} login - the channel username
|
* @param {string|null|undefined} login - the channel username
|
||||||
* @returns {Object} information about the requested user's latest broadcast
|
* @returns {Object} information about the requested user's latest broadcast
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getLastBroadcast(19571641, null));
|
* console.log(this.twitch_data.getLastBroadcast(19571641, null));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getLastBroadcast(id, login) {
|
async getLastBroadcast(id, login) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/last-broadcast.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/last-broadcast.gql'),
|
||||||
|
@ -322,21 +315,20 @@ export default class TwitchData extends Module {
|
||||||
// Broadcast ID
|
// Broadcast ID
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for the ID of the specified user's current broadcast. This ID will become the VOD ID. One of (id, login) MUST be specified
|
* Queries Apollo for the ID of the specified user's current broadcast. This ID will become the VOD ID. One of (id, login) MUST be specified
|
||||||
* @function getBroadcastID
|
* @function getBroadcastID
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
* @param {int|string|null|undefined} id - the channel id number (can be an integer string)
|
||||||
* @param {string|null|undefined} login - the channel username
|
* @param {string|null|undefined} login - the channel username
|
||||||
* @returns {Object} information about the current broadcast
|
* @returns {Object} information about the current broadcast
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getBroadcastID(null, "ninja"));
|
* console.log(this.twitch_data.getBroadcastID(null, "ninja"));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getBroadcastID(id, login) {
|
async getBroadcastID(id, login) {
|
||||||
const data = await this.queryApollo({
|
const data = await this.queryApollo({
|
||||||
query: await import(/* webpackChunkName: 'queries' */ './data/broadcast-id.gql'),
|
query: await import(/* webpackChunkName: 'queries' */ './data/broadcast-id.gql'),
|
||||||
|
@ -350,24 +342,36 @@ export default class TwitchData extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getChannelColor(id, login) {
|
||||||
|
const data = await this.queryApollo({
|
||||||
|
query: await import(/* webpackChunkName: 'queries' */ './data/user-color.gql'),
|
||||||
|
variables: {
|
||||||
|
id,
|
||||||
|
login
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return get('data.user.primaryColorHex', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Polls
|
// Polls
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for information about the specified poll.
|
* Queries Apollo for information about the specified poll.
|
||||||
* @function getPoll
|
* @function getPoll
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string} poll_id - the poll id number (can be an integer string)
|
* @param {int|string} poll_id - the poll id number (can be an integer string)
|
||||||
* @returns {Object} information about the specified poll
|
* @returns {Object} information about the specified poll
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getPoll(1337));
|
* console.log(this.twitch_data.getPoll(1337));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getPoll(poll_id) {
|
async getPoll(poll_id) {
|
||||||
const data = await this.queryApollo({
|
const data = await this.queryApollo({
|
||||||
query: await import(/* webpackChunkName: 'queries' */ './data/poll-get.gql'),
|
query: await import(/* webpackChunkName: 'queries' */ './data/poll-get.gql'),
|
||||||
|
@ -379,27 +383,26 @@ export default class TwitchData extends Module {
|
||||||
return get('data.poll', data);
|
return get('data.poll', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new poll
|
* Create a new poll
|
||||||
* @function createPoll
|
* @function createPoll
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string} channel_id - the channel id number (can be an integer string)
|
* @param {int|string} channel_id - the channel id number (can be an integer string)
|
||||||
* @param {string} title - the poll title
|
* @param {string} title - the poll title
|
||||||
* @param {string[]} choices - an array of poll choices
|
* @param {string[]} choices - an array of poll choices
|
||||||
* @param {Object} [options] - an object containing poll options
|
* @param {Object} [options] - an object containing poll options
|
||||||
* @param {int} [options.bits=0] - how many bits it costs to vote
|
* @param {int} [options.bits=0] - how many bits it costs to vote
|
||||||
* @param {int} [options.duration=60] - how long the poll will be held for, in seconds
|
* @param {int} [options.duration=60] - how long the poll will be held for, in seconds
|
||||||
* @param {bool} [options.subscriberMultiplier=false] - whether to activate subsriber 2x multiplier
|
* @param {bool} [options.subscriberMultiplier=false] - whether to activate subsriber 2x multiplier
|
||||||
* @param {bool} [options.subscriberOnly=false] - whether only subscribers may vote
|
* @param {bool} [options.subscriberOnly=false] - whether only subscribers may vote
|
||||||
* @returns {Object} poll data
|
* @returns {Object} poll data
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.createPoll(19571641, "Pick an option:", ["One", "Two", "Three"], {bits: 10, duration: 120, subscriberMultiplier: false, subscriberOnly: true}));
|
* console.log(this.twitch_data.createPoll(19571641, "Pick an option:", ["One", "Two", "Three"], {bits: 10, duration: 120, subscriberMultiplier: false, subscriberOnly: true}));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async createPoll(channel_id, title, choices, options = {}) {
|
async createPoll(channel_id, title, choices, options = {}) {
|
||||||
if ( typeof title !== 'string' )
|
if ( typeof title !== 'string' )
|
||||||
throw new TypeError('title must be string');
|
throw new TypeError('title must be string');
|
||||||
|
@ -433,20 +436,19 @@ export default class TwitchData extends Module {
|
||||||
return get('data.createPoll.poll', data);
|
return get('data.createPoll.poll', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Place specified poll into archive
|
* Place specified poll into archive
|
||||||
* @function archivePoll
|
* @function archivePoll
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} poll_id - the poll id number (can be an integer string)
|
* @param {int|string|null|undefined} poll_id - the poll id number (can be an integer string)
|
||||||
* @returns {Object} information about the specified poll
|
* @returns {Object} information about the specified poll
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.archivePoll(1337));
|
* console.log(this.twitch_data.archivePoll(1337));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async archivePoll(poll_id) {
|
async archivePoll(poll_id) {
|
||||||
const data = await this.mutate({
|
const data = await this.mutate({
|
||||||
mutation: await import(/* webpackChunkName: 'queries' */ './data/poll-archive.gql'),
|
mutation: await import(/* webpackChunkName: 'queries' */ './data/poll-archive.gql'),
|
||||||
|
@ -458,20 +460,19 @@ export default class TwitchData extends Module {
|
||||||
return get('data.archivePoll.poll', data);
|
return get('data.archivePoll.poll', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminate specified poll
|
* Terminate specified poll
|
||||||
* @function terminatePoll
|
* @function terminatePoll
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {int|string|null|undefined} poll_id - the poll id number (can be an integer string)
|
* @param {int|string|null|undefined} poll_id - the poll id number (can be an integer string)
|
||||||
* @returns {Object} information about the specified poll
|
* @returns {Object} information about the specified poll
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.archivePoll(1337));
|
* console.log(this.twitch_data.archivePoll(1337));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async terminatePoll(poll_id) {
|
async terminatePoll(poll_id) {
|
||||||
const data = await this.mutate({
|
const data = await this.mutate({
|
||||||
mutation: await import(/* webpackChunkName: 'queries' */ './data/poll-terminate.gql'),
|
mutation: await import(/* webpackChunkName: 'queries' */ './data/poll-terminate.gql'),
|
||||||
|
@ -501,7 +502,6 @@ export default class TwitchData extends Module {
|
||||||
*
|
*
|
||||||
* this.twitch_data.getStreamMeta(19571641, null).then(function(returnObj){console.log(returnObj);});
|
* this.twitch_data.getStreamMeta(19571641, null).then(function(returnObj){console.log(returnObj);});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getStreamMeta(id, login) {
|
getStreamMeta(id, login) {
|
||||||
return new Promise((s, f) => {
|
return new Promise((s, f) => {
|
||||||
if ( id ) {
|
if ( id ) {
|
||||||
|
@ -720,20 +720,19 @@ export default class TwitchData extends Module {
|
||||||
this._loadTags();
|
this._loadTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries Apollo for tag information
|
* Queries Apollo for tag information
|
||||||
* @function getTag
|
* @function getTag
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
*
|
*
|
||||||
* @param {int|string} id - the tag id
|
* @param {int|string} id - the tag id
|
||||||
* @param {bool} [want_description=false] - whether the description is also required
|
* @param {bool} [want_description=false] - whether the description is also required
|
||||||
* @returns {Promise} tag information
|
* @returns {Promise} tag information
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* this.twitch_data.getTag(50).then(function(returnObj){console.log(returnObj);});
|
* this.twitch_data.getTag(50).then(function(returnObj){console.log(returnObj);});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTag(id, want_description = false) {
|
getTag(id, want_description = false) {
|
||||||
// Make sure we weren't accidentally handed a tag object.
|
// Make sure we weren't accidentally handed a tag object.
|
||||||
if ( id && id.id )
|
if ( id && id.id )
|
||||||
|
@ -756,21 +755,20 @@ export default class TwitchData extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries the tag cache for tag information, queries Apollo on cache miss
|
* Queries the tag cache for tag information, queries Apollo on cache miss
|
||||||
* @function getTagImmediate
|
* @function getTagImmediate
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
*
|
*
|
||||||
* @param {int|string} id - the tag id
|
* @param {int|string} id - the tag id
|
||||||
* @param {getTagImmediateCallback} callback - callback function for use when requested tag information is not cached
|
* @param {getTagImmediateCallback} callback - callback function for use when requested tag information is not cached
|
||||||
* @param {bool} [want_description=false] - whether the tag description is required
|
* @param {bool} [want_description=false] - whether the tag description is required
|
||||||
* @returns {Object|null} tag information object, or on null, expect callback
|
* @returns {Object|null} tag information object, or on null, expect callback
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getTagImmediate(50));
|
* console.log(this.twitch_data.getTagImmediate(50));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTagImmediate(id, callback, want_description = false) {
|
getTagImmediate(id, callback, want_description = false) {
|
||||||
// Make sure we weren't accidentally handed a tag object.
|
// Make sure we weren't accidentally handed a tag object.
|
||||||
if ( id && id.id )
|
if ( id && id.id )
|
||||||
|
@ -789,28 +787,27 @@ export default class TwitchData extends Module {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function used when getTagImmediate experiences a cache miss
|
* Callback function used when getTagImmediate experiences a cache miss
|
||||||
* @callback getTagImmediateCallback
|
* @callback getTagImmediateCallback
|
||||||
* @param {int} tag_id - The tag ID number
|
* @param {int} tag_id - The tag ID number
|
||||||
* @param {Object} tag_object - the object containing tag data
|
* @param {Object} tag_object - the object containing tag data
|
||||||
* @param {Object} [error_object] - returned error information on tag data fetch failure
|
* @param {Object} [error_object] - returned error information on tag data fetch failure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Get top [n] tags
|
|
||||||
* @function getTopTags
|
|
||||||
* @memberof TwitchData
|
|
||||||
* @async
|
|
||||||
*
|
|
||||||
* @param {int|string} limit=50 - the number of tags to return (can be an integer string)
|
|
||||||
* @returns {string[]} an array containing the top tags up to the limit requested
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* console.log(this.twitch_data.getTopTags(20));
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get top [n] tags
|
||||||
|
* @function getTopTags
|
||||||
|
* @memberof TwitchData
|
||||||
|
* @async
|
||||||
|
*
|
||||||
|
* @param {int|string} limit=50 - the number of tags to return (can be an integer string)
|
||||||
|
* @returns {string[]} an array containing the top tags up to the limit requested
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* console.log(this.twitch_data.getTopTags(20));
|
||||||
|
*/
|
||||||
async getTopTags(limit = 50) {
|
async getTopTags(limit = 50) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/tags-top.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/tags-top.gql'),
|
||||||
|
@ -833,19 +830,18 @@ export default class TwitchData extends Module {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries tag languages
|
* Queries tag languages
|
||||||
* @function getLanguagesFromTags
|
* @function getLanguagesFromTags
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
*
|
*
|
||||||
* @param {int[]} tags - an array of tag IDs
|
* @param {int[]} tags - an array of tag IDs
|
||||||
* @returns {string[]} tag information
|
* @returns {string[]} tag information
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getLanguagesFromTags([50, 53, 58, 84]));
|
* console.log(this.twitch_data.getLanguagesFromTags([50, 53, 58, 84]));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLanguagesFromTags(tags, callback) { // TODO: actually use the callback
|
getLanguagesFromTags(tags, callback) { // TODO: actually use the callback
|
||||||
const out = [],
|
const out = [],
|
||||||
fn = callback ? debounce(() => {
|
fn = callback ? debounce(() => {
|
||||||
|
@ -865,22 +861,21 @@ export default class TwitchData extends Module {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search tags
|
* Search tags
|
||||||
* @function getMatchingTags
|
* @function getMatchingTags
|
||||||
* @memberof TwitchData
|
* @memberof TwitchData
|
||||||
* @async
|
* @async
|
||||||
*
|
*
|
||||||
* @param {string} query - the search string
|
* @param {string} query - the search string
|
||||||
* @param {string} [locale] - the locale to return tags from
|
* @param {string} [locale] - the locale to return tags from
|
||||||
* @param {string} [category=null] - the category to return tags from
|
* @param {string} [category=null] - the category to return tags from
|
||||||
* @returns {string[]} an array containing tags that match the query string
|
* @returns {string[]} an array containing tags that match the query string
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* console.log(this.twitch_data.getMatchingTags("Rainbo"));
|
* console.log(this.twitch_data.getMatchingTags("Rainbo"));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getMatchingTags(query, locale, category = null) {
|
async getMatchingTags(query, locale, category = null) {
|
||||||
if ( ! locale )
|
if ( ! locale )
|
||||||
locale = this.locale;
|
locale = this.locale;
|
||||||
|
|
|
@ -74,6 +74,13 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz--tooltip-explain {
|
||||||
|
width: 30rem;
|
||||||
|
font-weight: 100;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.ffz__tooltip {
|
.ffz__tooltip {
|
||||||
z-index: 999999999;
|
z-index: 999999999;
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue