mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.20.89
* Added: Setting to display timestamps on additional types of chat messages. (Closes #983) * Changed: Do not set a Chat Width by default. * Changed: Have WebMunch dump a list of possible webpack bundle names when failing to find webpack. * Fixed: Appearance issues caused by Twitch's continual migration to procedural CSS class names, requiring duplicate CSS to achieve a native look. * Fixed: Ambiguous input field names in some FFZ Control Center widgets. (Closes #1017) * Fixed: Stop using Algolia for tag search.
This commit is contained in:
parent
fa33491eec
commit
ae90b8e4fe
35 changed files with 306 additions and 222 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.20.87",
|
||||
"version": "4.20.89",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--addon-info tw-elevation-1 tw-c-background-base tw-border tw-border-radius-large tw-pd-1 tw-mg-b-1 tw-flex tw-flex-nowrap">
|
||||
<div class="tw-flex tw-flex-column tw-align-center tw-flex-shrink-0 tw-mg-r-1">
|
||||
<div class="tw-card-img--size-6 tw-overflow-hidden tw-mg-b-1">
|
||||
<div class="ffz-card-img--size-6 tw-overflow-hidden tw-mg-b-1">
|
||||
<img :src="icon" class="tw-image">
|
||||
</div>
|
||||
|
||||
|
|
|
@ -191,16 +191,18 @@ export default {
|
|||
},
|
||||
|
||||
data() {
|
||||
const this_id = `badge-term$${id++}`;
|
||||
|
||||
if ( this.adding )
|
||||
return {
|
||||
id: id++,
|
||||
id: this_id,
|
||||
deleting: false,
|
||||
editing: true,
|
||||
edit_data: deep_copy(this.term)
|
||||
};
|
||||
|
||||
return {
|
||||
id: id++,
|
||||
id: this_id,
|
||||
deleting: false,
|
||||
editing: false,
|
||||
edit_data: null
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
:href="commit.author.html_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tw-inline-flex tw-align-items-center tw-link tw-link--inherit tw-mg-x-05 ffz-tooltip"
|
||||
class="tw-inline-flex tw-align-items-center ffz-link ffz-link--inherit tw-mg-x-05 ffz-tooltip"
|
||||
data-tooltip-type="link"
|
||||
>
|
||||
<figure
|
||||
|
@ -77,7 +77,7 @@
|
|||
v-if="commit.hash"
|
||||
class="tw-font-size-8 tw-c-text-alt-2"
|
||||
>
|
||||
@<a :href="commit.link" target="_blank" rel="noopener noreferrer" class="tw-link tw-link--inherit ffz-tooltip" data-tooltip-type="link">{{ commit.hash }}</a>
|
||||
@<a :href="commit.link" target="_blank" rel="noopener noreferrer" class="ffz-link ffz-link--inherit ffz-tooltip" data-tooltip-type="link">{{ commit.hash }}</a>
|
||||
</div>
|
||||
<time
|
||||
v-if="commit.date"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="tw-pd-x-1 tw-pd-y-05">
|
||||
<div class="tw-card tw-relative">
|
||||
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row">
|
||||
<div class="tw-card-img tw-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<div class="ffz-card-img ffz-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<aspect :ratio="1/1.33">
|
||||
<img
|
||||
:alt="slot.item.displayName"
|
||||
|
@ -55,7 +55,7 @@
|
|||
<a
|
||||
v-if="can_link"
|
||||
:href="`/directory/game/${i}`"
|
||||
class="tw-link"
|
||||
class="ffz-link"
|
||||
@click.prevent="handleLink(i)"
|
||||
>
|
||||
{{ i }}
|
||||
|
|
|
@ -70,14 +70,14 @@
|
|||
:key="addon.key"
|
||||
class="tw-mg-b-05 tw-flex tw-align-items-center"
|
||||
>
|
||||
<div class="tw-card-img--size-4 tw-overflow-hidden tw-mg-r-1">
|
||||
<div class="ffz-card-img--size-4 tw-overflow-hidden tw-mg-r-1">
|
||||
<img :src="addon.icon" class="tw-image">
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
v-if="addon.enabled && addon.settings"
|
||||
href="#"
|
||||
class="tw-strong tw-link--inherit"
|
||||
class="tw-strong ffz-link--inherit"
|
||||
@click.prevent="item.requestPage(addon.settings)"
|
||||
>
|
||||
{{ addon.name_i18n ? t(addon.name_i18n, addon.name) : addon.name }}
|
||||
|
@ -114,14 +114,14 @@
|
|||
:key="addon.key"
|
||||
class="tw-mg-b-05 tw-flex tw-align-items-center"
|
||||
>
|
||||
<div class="tw-card-img--size-4 tw-overflow-hidden tw-mg-r-1">
|
||||
<div class="ffz-card-img--size-4 tw-overflow-hidden tw-mg-r-1">
|
||||
<img :src="addon.icon" class="tw-image">
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
v-if="addon.enabled && addon.settings"
|
||||
href="#"
|
||||
class="tw-strong tw-link--inherit"
|
||||
class="tw-strong ffz-link--inherit"
|
||||
@click.prevent="item.requestPage(addon.settings)"
|
||||
>
|
||||
{{ addon.name_i18n ? t(addon.name_i18n, addon.name) : addon.name }}
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<a
|
||||
v-if="version.commit"
|
||||
:href="`https://www.github.com/FrankerFaceZ/FrankerFaceZ/commit/${version.commit}`"
|
||||
class="tw-link tw-link--inherit"
|
||||
class="ffz-link ffz-link--inherit"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
|
|
@ -262,16 +262,18 @@ export default {
|
|||
},
|
||||
|
||||
data() {
|
||||
const this_id = `term$${id++}`;
|
||||
|
||||
if ( this.adding )
|
||||
return {
|
||||
id: id++,
|
||||
id: this_id,
|
||||
deleting: false,
|
||||
editing: true,
|
||||
edit_data: deep_copy(this.term)
|
||||
};
|
||||
|
||||
return {
|
||||
id: id++,
|
||||
id: this_id,
|
||||
deleting: false,
|
||||
editing: false,
|
||||
edit_data: null
|
||||
|
|
|
@ -189,7 +189,7 @@ export default {
|
|||
} else
|
||||
this.source_value = undefined;
|
||||
|
||||
this.has_value = this.profile.has(this.item.setting);
|
||||
this.has_value = this.profile && this.profile.has(this.item.setting);
|
||||
if ( ! this.has_value )
|
||||
this.value = this.isInherited ? this.source_value : this.default_value;
|
||||
}, 0);
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
:href="`/${login}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tw-interactive tw-link tw-link--hover-color-inherit tw-link--inherit"
|
||||
class="tw-interactive ffz-link ffz-link--hover-color-inherit ffz-link--inherit"
|
||||
>
|
||||
{{ displayName }}
|
||||
</a>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{{ t(type.i18n, type.title) }}
|
||||
</label>
|
||||
|
||||
<div class="ffz--search-avatar tw-mg-x-05 tw-card-img--size-2">
|
||||
<div class="ffz--search-avatar tw-mg-x-05 ffz-card-img--size-2">
|
||||
<aspect :ratio="1/1.33">
|
||||
<img
|
||||
v-if="current"
|
||||
|
@ -29,7 +29,7 @@
|
|||
<div class="tw-pd-x-1 tw-pd-y-05">
|
||||
<div class="tw-card tw-relative">
|
||||
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row">
|
||||
<div class="tw-card-img tw-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<div class="ffz-card-img ffz-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<aspect :ratio="1/1.33">
|
||||
<img
|
||||
:alt="slot.item.displayName"
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<div class="tw-pd-x-1 tw-pd-y-05">
|
||||
<div class="tw-card tw-relative">
|
||||
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row">
|
||||
<div class="tw-card-img tw-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<div class="ffz-card-img ffz-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
|
||||
<aspect :ratio="1">
|
||||
<img
|
||||
:alt="slot.item.displayName"
|
||||
|
|
|
@ -89,7 +89,7 @@ export default class Line extends Module {
|
|||
t.chat.badges.render(msg, createElement)
|
||||
}</span>
|
||||
<a
|
||||
class="clip-chat__message-author tw-font-size-5 tw-link notranslate"
|
||||
class="clip-chat__message-author tw-font-size-5 ffz-link notranslate"
|
||||
href={`https://www.twitch.tv/${user.login}/clips`}
|
||||
style={{color}}
|
||||
>
|
||||
|
|
|
@ -199,7 +199,7 @@ export default class Channel extends Module {
|
|||
if ( ! inst._ffz_tips ) {
|
||||
const tt = this.resolve('tooltips');
|
||||
if ( tt ) {
|
||||
inst._ffz_tips = tt._createInstance(el, 'tw-link', 'link', tt.getRoot());
|
||||
inst._ffz_tips = tt._createInstance(el, 'ffz-link', 'link', tt.getRoot());
|
||||
inst._ffz_tip_el = el;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -440,7 +440,7 @@ export default class ChatHook extends Module {
|
|||
});
|
||||
|
||||
this.settings.add('chat.width', {
|
||||
default: 340,
|
||||
default: null,
|
||||
ui: {
|
||||
path: 'Chat > Appearance >> General @{"sort": -1}',
|
||||
title: 'Width',
|
||||
|
@ -449,20 +449,28 @@ export default class ChatHook extends Module {
|
|||
process(val) {
|
||||
val = parseInt(val, 10);
|
||||
if ( isNaN(val) || ! isFinite(val) || val <= 0 )
|
||||
return 340;
|
||||
return null;
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.effective-width', {
|
||||
requires: ['chat.width', 'context.ui.rightColumnWidth'],
|
||||
process(ctx) {
|
||||
const val = ctx.get('chat.width');
|
||||
return val == null ? (ctx.get('context.ui.rightColumnWidth') || 340) : val;
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.use-width', {
|
||||
requires: ['chat.width', 'context.ui.rightColumnExpanded', 'context.isWatchParty'],
|
||||
process(ctx) {
|
||||
if ( ! ctx.get('context.ui.rightColumnExpanded') || ctx.get('context.isWatchParty') )
|
||||
return false;
|
||||
|
||||
return ctx.get('chat.width') != 340;
|
||||
return ctx.get('chat.width') != null;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -511,6 +519,16 @@ export default class ChatHook extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.extra-timestamps', {
|
||||
default: true,
|
||||
ui: {
|
||||
path: 'Chat > Appearance >> Chat Lines',
|
||||
title: 'Display timestamps on notices.',
|
||||
description: 'When enabled, timestamps will be displayed on point redemptions, subscriptions, etc.',
|
||||
component: 'setting-check-box'
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.subs.show', {
|
||||
default: 3,
|
||||
ui: {
|
||||
|
@ -670,7 +688,7 @@ export default class ChatHook extends Module {
|
|||
cancelAnimationFrame(this._update_css_waiter);
|
||||
this._update_css_waiter = null;
|
||||
|
||||
const width = this.chat.context.get('chat.width'),
|
||||
const width = this.chat.context.get('chat.effective-width'),
|
||||
action_size = this.chat.context.get('chat.actions.size'),
|
||||
ts_size = this.chat.context.get('chat.timestamp-size'),
|
||||
size = this.chat.context.get('chat.font-size'),
|
||||
|
@ -808,7 +826,7 @@ export default class ChatHook extends Module {
|
|||
this.chat.context.on('changed:chat.banners.prediction', this.cleanHighlights, this);
|
||||
|
||||
this.chat.context.on('changed:chat.subs.gift-banner', () => this.GiftBanner.forceUpdate(), this);
|
||||
this.chat.context.on('changed:chat.width', this.updateChatCSS, this);
|
||||
this.chat.context.on('changed:chat.effective-width', this.updateChatCSS, this);
|
||||
this.settings.main_context.on('changed:chat.use-width', this.updateChatCSS, this);
|
||||
this.chat.context.on('changed:chat.actions.size', this.updateChatCSS, this);
|
||||
this.chat.context.on('changed:chat.font-size', this.updateChatCSS, this);
|
||||
|
|
|
@ -527,7 +527,8 @@ other {# messages were deleted by a moderator.}
|
|||
] : user_block)
|
||||
];
|
||||
|
||||
let cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}${twitch_clickable ? ' tw-relative' : ''}`,
|
||||
let extra_ts,
|
||||
cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}${twitch_clickable ? ' tw-relative' : ''}`,
|
||||
out = (tokens.length || ! msg.ffz_type) ? [
|
||||
(this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
|
@ -570,6 +571,9 @@ other {# messages were deleted by a moderator.}
|
|||
}, JSON.stringify([tokens, msg.emotes], null, 2))*/
|
||||
] : null;
|
||||
|
||||
if ( out == null )
|
||||
extra_ts = t.chat.context.get('chat.extra-timestamps');
|
||||
|
||||
if ( msg.ffz_type === 'sub_mystery' ) {
|
||||
const mystery = msg.mystery;
|
||||
if ( mystery )
|
||||
|
@ -640,6 +644,9 @@ other {# messages were deleted by a moderator.}
|
|||
className: `ffz-i-star${msg.sub_anon ? '-empty' : ''} tw-mg-r-05`
|
||||
}),
|
||||
e('div', null, [
|
||||
out ? null : extra_ts && (this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
(out || msg.sub_anon) ? null : t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
||||
sub_msg
|
||||
]),
|
||||
|
@ -710,6 +717,9 @@ other {# messages were deleted by a moderator.}
|
|||
className: 'ffz-i-star tw-mg-r-05'
|
||||
}),
|
||||
e('div', null, [
|
||||
out ? null : extra_ts && (this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
(out || msg.sub_anon) ? null : t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
||||
sub_msg
|
||||
])
|
||||
|
@ -772,6 +782,9 @@ other {# messages were deleted by a moderator.}
|
|||
className: `ffz-i-${plan.prime ? 'crown' : 'star'} tw-mg-r-05`
|
||||
}),
|
||||
e('div', null, [
|
||||
out ? null : extra_ts && (this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
out ? null : t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
||||
sub_msg
|
||||
])
|
||||
|
@ -804,6 +817,9 @@ other {# messages were deleted by a moderator.}
|
|||
if ( system_msg ) {
|
||||
cls = `ffz-notice-line user-notice-line tw-pd-y-05 tw-pd-r-2 ffz--ritual-line${show_class ? ' ffz--deleted-message' : ''}${twitch_clickable ? ' tw-relative' : ''}`;
|
||||
out = [
|
||||
out ? null : extra_ts && (this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
system_msg,
|
||||
out && e('div', {
|
||||
className: 'chat-line--inline chat-line__message',
|
||||
|
@ -825,6 +841,9 @@ other {# messages were deleted by a moderator.}
|
|||
cls = `ffz-notice-line ffz--points-line tw-pd-l-1 tw-pd-y-05 tw-pd-r-2${ffz_highlight ? ' ffz-custom-color ffz--points-highlight' : ''}${show_class ? ' ffz--deleted-message' : ''}${twitch_clickable ? ' tw-relative' : ''}`;
|
||||
out = [
|
||||
e('div', {className: 'tw-c-text-alt-2'}, [
|
||||
out ? null : extra_ts && (this.props.showTimestamps || this.props.isHistorical) && e('span', {
|
||||
className: 'chat-line__timestamp'
|
||||
}, t.chat.formatTime(msg.timestamp)),
|
||||
out ? null : t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
||||
out ?
|
||||
t.i18n.tList('chat.points.redeemed', 'Redeemed {reward} {cost}', {reward, cost}) :
|
||||
|
|
|
@ -298,7 +298,7 @@ export default class SettingsMenu extends Module {
|
|||
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">
|
||||
website: (<a href="https://www.frankerfacez.com/donate" class="ffz-link" rel="noopener noreferrer" target="_blank">
|
||||
{this.i18n.t('chat.ffz-badge.site-link', 'FrankerFaceZ website')}
|
||||
</a>)
|
||||
})}
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
width: calc(var(--ffz-chat-width) - 2rem) !important;
|
||||
}
|
||||
|
||||
.channel-root {
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
body .whispers--theatre-mode.whispers--right-column-expanded-beside {
|
||||
right: var(--ffz-chat-width);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
.right-column {
|
||||
order: 1;
|
||||
z-index: 2;
|
||||
|
||||
#root &:not(.right-column--collapsed) {
|
||||
width: var(--ffz-chat-width);
|
||||
}
|
||||
}
|
||||
|
||||
.channel-root {
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
#sideNav .collapse-toggle {
|
||||
|
|
|
@ -267,11 +267,11 @@ export default class Directory extends SiteModule {
|
|||
channel_url = `/${channel.login}`,
|
||||
game_url = game && `/directory/game/${stream.game.name}`,
|
||||
|
||||
user_link = <a href={channel_url} data-href={channel_url} title={channel.displayName} class="tw-link tw-link--inherit" onClick={t.routeClick}>{channel.displayName}</a>,
|
||||
game_link = game && <a href={game_url} data-href={game_url} title={game.name} class="tw-link tw-link--inherit" onClick={t.routeClick}>{game.name}</a>;
|
||||
user_link = <a href={channel_url} data-href={channel_url} title={channel.displayName} class="ffz-link ffz-link--inherit" onClick={t.routeClick}>{channel.displayName}</a>,
|
||||
game_link = game && <a href={game_url} data-href={game_url} title={game.name} class="ffz-link ffz-link--inherit" onClick={t.routeClick}>{game.name}</a>;
|
||||
|
||||
return (<div>
|
||||
<a href={channel_url} data-href={channel_url} class="tw-link tw-link--inherit" data-test-selector="preview-card-titles__primary-link" onClick={t.routeClick}>
|
||||
<a href={channel_url} data-href={channel_url} class="ffz-link ffz-link--inherit" data-test-selector="preview-card-titles__primary-link" onClick={t.routeClick}>
|
||||
<h3 class="tw-ellipsis tw-font-size-5 tw-strong" title={stream.title}>{stream.title}</h3>
|
||||
</a>
|
||||
<div class="preview-card-titles__subtitle-wrapper">
|
||||
|
@ -295,7 +295,7 @@ export default class Directory extends SiteModule {
|
|||
nodes.length > 1 ?
|
||||
t.i18n.t('directory.hosted.by-many', 'Hosted by {count,number} channel{count,en_plural}', nodes.length) :
|
||||
t.i18n.tList('directory.hosted.by-one', 'Hosted by {user}', {
|
||||
user: <a href={`/${nodes[0].login}`} data-href={`/${nodes[0].login}`} title={nodes[0].displayName} class="tw-link tw-link--inherit" onClick={t.routeClick}>{nodes[0].displayName}</a>
|
||||
user: <a href={`/${nodes[0].login}`} data-href={`/${nodes[0].login}`} title={nodes[0].displayName} class="ffz-link ffz-link--inherit" onClick={t.routeClick}>{nodes[0].displayName}</a>
|
||||
})
|
||||
}</p>
|
||||
</div>
|
||||
|
|
|
@ -93,6 +93,20 @@ export default class WebMunch extends Module {
|
|||
if ( ! name ) {
|
||||
if ( attempts > 240 ) {
|
||||
this.log.error("Unable to find webpack's loader after one minute.");
|
||||
|
||||
try {
|
||||
const possibilities = [];
|
||||
for(const key of Object.keys(window))
|
||||
if ( has(window, key) && typeof key === 'string' && /webpack/i.test(key) && ! /ffz/i.test(key) )
|
||||
possibilities.push(key);
|
||||
|
||||
if ( possibilities.length )
|
||||
this.log.info('Possible Matches: ', possibilities.join(', '));
|
||||
else
|
||||
this.log.info('No possible matches found.');
|
||||
|
||||
} catch(err) { /* no-op */ }
|
||||
|
||||
this._resolveLoadWait(true);
|
||||
return;
|
||||
}
|
||||
|
|
11
src/utilities/data/search-tags.gql
Normal file
11
src/utilities/data/search-tags.gql
Normal file
|
@ -0,0 +1,11 @@
|
|||
query FFZ_SearchLiveTags($query: String!, $categoryID: ID, $limit: Int) {
|
||||
searchLiveTags(userQuery: $query, categoryID: $categoryID, limit: $limit) {
|
||||
id
|
||||
isAutomated
|
||||
isLanguageTag
|
||||
localizedDescription
|
||||
localizedName
|
||||
scope
|
||||
tagName
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
query FFZ_FetchTags($ids: [ID!]) {
|
||||
contentTags(ids: $ids) {
|
||||
id
|
||||
isAutomated
|
||||
isLanguageTag
|
||||
tagName
|
||||
localizedName
|
||||
localizedDescription
|
||||
localizedName
|
||||
scope
|
||||
tagName
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
query FFZ_TopTags($limit: Int) {
|
||||
topTags(limit: $limit) {
|
||||
id
|
||||
isAutomated
|
||||
isLanguageTag
|
||||
tagName
|
||||
localizedName
|
||||
localizedDescription
|
||||
localizedName
|
||||
scope
|
||||
tagName
|
||||
}
|
||||
}
|
|
@ -825,7 +825,7 @@ TOKEN_TYPES.link = function(token, createElement, ctx) {
|
|||
klass.push(`tw-block tw-border tw-border-radius-large tw-mg-y-05 tw-pd-05`);
|
||||
|
||||
if ( token.no_color )
|
||||
klass.push(`tw-link--inherit`);
|
||||
klass.push(`ffz-link--inherit`);
|
||||
|
||||
if ( ctx.vue )
|
||||
return createElement('a', {
|
||||
|
|
|
@ -6,65 +6,10 @@
|
|||
// ============================================================================
|
||||
|
||||
import Module from 'utilities/module';
|
||||
import {get, debounce, generateUUID} from 'utilities/object';
|
||||
import {get, debounce} from 'utilities/object';
|
||||
|
||||
const LANGUAGE_MATCHER = /^auto___lang_(\w+)$/;
|
||||
|
||||
const ALGOLIA_LANGUAGES = {
|
||||
bg: 'bg-bg',
|
||||
cs: 'cs-cz',
|
||||
da: 'da-dk',
|
||||
de: 'de-de',
|
||||
el: 'el-gr',
|
||||
en: 'en-us',
|
||||
es: 'es-es',
|
||||
'es-mx': 'es-mx',
|
||||
fi: 'fi-fi',
|
||||
fr: 'fr-fr',
|
||||
hu: 'hu-hu',
|
||||
it: 'it-it',
|
||||
ja: 'ja-jp',
|
||||
ko: 'ko-kr',
|
||||
nl: 'nl-nl',
|
||||
no: 'no-no',
|
||||
pl: 'pl-pl',
|
||||
'pt-br': 'pt-br',
|
||||
pt: 'pt-pt',
|
||||
ro: 'ro-ro',
|
||||
ru: 'ru-ru',
|
||||
sk: 'sk-sk',
|
||||
sv: 'sv-se',
|
||||
th: 'th-th',
|
||||
tr: 'tr-tr',
|
||||
vi: 'vi-vn',
|
||||
'zh-cn': 'zh-cn',
|
||||
'zh-tw': 'zh-tw'
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Algolia Language code for a given locale
|
||||
* @function getAlgoliaLanguage
|
||||
*
|
||||
* @param {string} locale - a string representation of a locale
|
||||
* @returns {string} the Algolia Language code for the given locale
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* console.log(getAlgoliaLanguage('en'));
|
||||
*/
|
||||
function getAlgoliaLanguage(locale) {
|
||||
if ( ! locale )
|
||||
return ALGOLIA_LANGUAGES.en;
|
||||
|
||||
locale = locale.toLowerCase();
|
||||
if ( ALGOLIA_LANGUAGES[locale] )
|
||||
return ALGOLIA_LANGUAGES[locale];
|
||||
|
||||
locale = locale.split('-')[0];
|
||||
return ALGOLIA_LANGUAGES[locale] || ALGOLIA_LANGUAGES.en;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TwitchData is a container for getting different types of Twitch data
|
||||
* @class TwitchData
|
||||
|
@ -132,34 +77,6 @@ export default class TwitchData extends Module {
|
|||
return session && session.locale || 'en-US'
|
||||
}
|
||||
|
||||
async searchClient() {
|
||||
if ( this._search )
|
||||
return this._search;
|
||||
|
||||
const apollo = this.apollo.client,
|
||||
core = this.site.getCore(),
|
||||
web_munch = this.resolve('web_munch');
|
||||
|
||||
if ( ! web_munch )
|
||||
return null;
|
||||
|
||||
await web_munch.enable();
|
||||
|
||||
const SearchClient = await this.web_munch.findModule('algolia-search');
|
||||
if ( ! SearchClient || ! apollo || ! core )
|
||||
return null;
|
||||
|
||||
this._search = new SearchClient({
|
||||
appId: core.config.algoliaApplicationID,
|
||||
apiKey: core.config.algoliaAPIKey,
|
||||
apolloClient: apollo,
|
||||
logger: core.logger,
|
||||
config: core.config,
|
||||
stats: core.stats
|
||||
});
|
||||
|
||||
return this._search;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Badges
|
||||
|
@ -350,6 +267,7 @@ export default class TwitchData extends Module {
|
|||
return get('data.user.lastBroadcast', data);
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// Broadcast ID
|
||||
// ========================================================================
|
||||
|
@ -665,27 +583,28 @@ export default class TwitchData extends Module {
|
|||
if ( ! node || ! node.id || ! node.tagName || ! node.localizedName )
|
||||
return;
|
||||
|
||||
let old = null;
|
||||
if ( this.tag_cache.has(node.id) )
|
||||
old = this.tag_cache.get(old);
|
||||
let tag = this.tag_cache.get(node.id);
|
||||
if ( ! tag ) {
|
||||
const match = node.isLanguageTag && LANGUAGE_MATCHER.exec(node.tagName),
|
||||
lang = match && match[1] || null;
|
||||
|
||||
const match = node.isLanguageTag && LANGUAGE_MATCHER.exec(node.tagName),
|
||||
lang = match && match[1] || null;
|
||||
tag = {
|
||||
id: node.id,
|
||||
value: node.id,
|
||||
is_auto: node.isAutomated,
|
||||
is_language: node.isLanguageTag,
|
||||
language: lang,
|
||||
name: node.tagName,
|
||||
scope: node.scope
|
||||
};
|
||||
|
||||
const new_tag = {
|
||||
id: node.id,
|
||||
value: node.id,
|
||||
is_language: node.isLanguageTag,
|
||||
language: lang,
|
||||
name: node.tagName,
|
||||
label: node.localizedName
|
||||
};
|
||||
this.tag_cache.set(node.id, tag);
|
||||
}
|
||||
|
||||
if ( node.localizedName )
|
||||
tag.label = node.localizedName;
|
||||
if ( node.localizedDescription )
|
||||
new_tag.description = node.localizedDescription;
|
||||
|
||||
const tag = old ? Object.assign(old, new_tag) : new_tag;
|
||||
this.tag_cache.set(tag.id, tag);
|
||||
tag.description = node.localizedDescription;
|
||||
|
||||
if ( dispatch && tag.description && this._waiting_tags.has(tag.id) ) {
|
||||
const promises = this._waiting_tags.get(tag.id);
|
||||
|
@ -907,100 +826,36 @@ export default class TwitchData extends Module {
|
|||
* @async
|
||||
*
|
||||
* @param {string} query - the search string
|
||||
* @param {string} [locale] - the locale to return tags from
|
||||
* @param {string} [locale] - UNUSED. the locale 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
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* console.log(this.twitch_data.getMatchingTags("Rainbo"));
|
||||
* console.log(await this.twitch_data.getMatchingTags("Rainbo"));
|
||||
*/
|
||||
async getMatchingTags(query, locale, category = null) {
|
||||
if ( ! locale )
|
||||
locale = this.locale;
|
||||
/*if ( ! locale )
|
||||
locale = this.locale;*/
|
||||
|
||||
const client = await this.searchClient();
|
||||
const data = await this.queryApollo({
|
||||
query: await import(/* webpackChunkName: 'queries' */ './data/search-tags.gql'),
|
||||
variables: {
|
||||
query,
|
||||
categoryID: category || null,
|
||||
limit: 100
|
||||
}
|
||||
});
|
||||
|
||||
locale = getAlgoliaLanguage(locale);
|
||||
|
||||
let nodes;
|
||||
|
||||
if ( category ) {
|
||||
const data = await client.queryForType(
|
||||
'stream_tag', query, generateUUID(), {
|
||||
hitsPerPage: 100,
|
||||
faceFilters: [
|
||||
`category_id:${category}`
|
||||
],
|
||||
restrictSearchableAttributes: [
|
||||
`localizations.${locale}`,
|
||||
'tag_name'
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
nodes = get('streamTags.hits', data);
|
||||
|
||||
} else {
|
||||
const data = await client.queryForType(
|
||||
'tag', query, generateUUID(), {
|
||||
hitsPerPage: 100,
|
||||
facetFilters: [
|
||||
['tag_scope:SCOPE_ALL', 'tag_scope:SCOPE_CATEGORY']
|
||||
],
|
||||
restrictSearchableAttributes: [
|
||||
`localizations.${locale}`,
|
||||
'tag_name'
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
nodes = get('tags.hits', data);
|
||||
}
|
||||
|
||||
if ( ! Array.isArray(nodes) )
|
||||
const nodes = data?.data?.searchLiveTags;
|
||||
if ( ! Array.isArray(nodes) || ! nodes.length )
|
||||
return [];
|
||||
|
||||
const out = [], seen = new Set;
|
||||
const out = [];
|
||||
for(const node of nodes) {
|
||||
const tag_id = node.tag_id || node.objectID;
|
||||
if ( ! node || seen.has(tag_id) )
|
||||
continue;
|
||||
|
||||
seen.add(tag_id);
|
||||
if ( ! this.tag_cache.has(tag_id) ) {
|
||||
const match = node.tag_name && LANGUAGE_MATCHER.exec(node.tag_name),
|
||||
lang = match && match[1] || null;
|
||||
|
||||
const tag = {
|
||||
id: tag_id,
|
||||
value: tag_id,
|
||||
is_language: lang != null,
|
||||
language: lang,
|
||||
label: node.localizations && (node.localizations[locale] || node.localizations['en-us']) || node.tag_name
|
||||
};
|
||||
|
||||
if ( node.description_localizations ) {
|
||||
const desc = node.description_localizations[locale] || node.description_localizations['en-us'];
|
||||
if ( desc )
|
||||
tag.description = desc;
|
||||
}
|
||||
|
||||
this.tag_cache.set(tag.id, tag);
|
||||
const tag = this.memorizeTag(node);
|
||||
if ( tag )
|
||||
out.push(tag);
|
||||
|
||||
} else {
|
||||
const tag = this.tag_cache.get(tag_id);
|
||||
if ( ! tag.description && node.description_localizations ) {
|
||||
const desc = node.description_localizations[locale] || node.description_localizations['en-us'];
|
||||
if ( desc ) {
|
||||
tag.description = desc;
|
||||
this.tag_cache.set(tag.id, tag);
|
||||
}
|
||||
}
|
||||
|
||||
out.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@import 'widgets';
|
||||
@import 'dialog';
|
||||
|
||||
@import 'input/index';
|
||||
@import 'native/index';
|
||||
@import 'structure/index';
|
||||
|
||||
@import 'chat';
|
||||
|
|
15
styles/native/card.scss
Normal file
15
styles/native/card.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
.ffz-card-img {
|
||||
background-color: var(--color-background-placeholder);;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
|
||||
&--size-2 { width: 2rem }
|
||||
&--size-3 { width: 3rem }
|
||||
&--size-4 { width: 4rem }
|
||||
&--size-6 { width: 6rem }
|
||||
&--size-8 { width: 8rem }
|
||||
&--size-12 { width: 12rem }
|
||||
&--size-16 { width: 16rem }
|
||||
&--size-24 { width: 24rem }
|
||||
&--size-32 { width: 32rem }
|
||||
}
|
|
@ -2,3 +2,6 @@
|
|||
@import "checkbox";
|
||||
@import "interactable";
|
||||
@import "text";
|
||||
@import "link";
|
||||
@import "card";
|
||||
@import "tag";
|
67
styles/native/link.scss
Normal file
67
styles/native/link.scss
Normal file
|
@ -0,0 +1,67 @@
|
|||
.ffz-link {
|
||||
color: var(--color-text-link);
|
||||
text-decoration: none;
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
color: var(--color-text-link-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:active { color: var(--color-text-link-active); }
|
||||
&[data-focus-visible-added], &:focus { color: var(--color-text-link-focus); }
|
||||
&:visited { color: var(--color-text-link-visited); }
|
||||
|
||||
&--overlay {
|
||||
color: var(--color-text-overlay-link);
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
color: var(--color-text-overlay-link-hover);
|
||||
}
|
||||
|
||||
&:active { color: var(--color-text-overlay-link-active) }
|
||||
&:focus, &[data-focus-visible-added] { color: var(--color-text-overlay-link-focus) }
|
||||
&:visited { color: var(--color-text-overlay-link-visited) }
|
||||
}
|
||||
|
||||
&--underline { text-decoration: underline; }
|
||||
|
||||
&--inherit {
|
||||
&, &:visited, &:focus, &[data-focus-visible-added], &:active {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
color: var(--color-text-link-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.tw-root--hover &--hover-color-inherit:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tw-root--hover &--hover-underline-none {
|
||||
&, &:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--button {
|
||||
&:active {
|
||||
outline: none
|
||||
}
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: .5;
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
cursor: not-allowed;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
62
styles/native/tag.scss
Normal file
62
styles/native/tag.scss
Normal file
|
@ -0,0 +1,62 @@
|
|||
.ffz-tag {
|
||||
background-color: var(--color-background-tag-default);
|
||||
border: var(--border-width-tag) solid transparent;
|
||||
color: var(--color-text-tag);
|
||||
height: 2rem;
|
||||
|
||||
&:not(:disabled) {
|
||||
&:focus, &[data-focus-visible-added] {
|
||||
background: var(--color-background-tag-hover);
|
||||
border: var(--border-width-tag) solid var(--color-border-input-focus);
|
||||
}
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
background: var(--color-background-tag-hover);
|
||||
color: var(--color-text-tag);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--color-background-tag-active);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 0 .8rem;
|
||||
|
||||
&--icon {
|
||||
padding-right: .4rem
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
}
|
||||
|
||||
&--overlay {
|
||||
background: rgba(0,0,0,0.3);
|
||||
border: var(--border-width-tag) solid var(--color-border-input-overlay);
|
||||
color: var(--color-text-overlay);
|
||||
|
||||
&:not(:disabled) {
|
||||
&:focus, &[data-focus-visible-added] {
|
||||
border: var(--border-width-tag) solid var(--color-border-input-overlay-focus);
|
||||
}
|
||||
|
||||
.tw-root--hover &:hover {
|
||||
background: var(--color-background-interactable-overlay-hover);
|
||||
color: var(--color-text-overlay);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--color-background-interactable-overlay-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue