{data.title || t.i18n.t('emote-menu.unknown', 'Unknown Source')}
@@ -289,12 +364,12 @@ export default class EmoteMenu extends Module {
{data.source || 'FrankerFaceZ'}
-
- {collapsed || this.renderBody()}
+ ) : null}
+ {collapsed || this.renderBody(show_heading)}
)
}
- renderBody() {
+ renderBody(show_sources) {
const data = this.props.data,
filtered = this.props.filtered,
lock = data.locks && data.locks[this.state.unlocked],
@@ -302,6 +377,7 @@ export default class EmoteMenu extends Module {
key={emote.id}
onClickEmote={this.props.onClickEmote}
data={emote}
+ source={show_sources}
locked={emote.locked && (! lock || ! lock.emotes.has(emote.id))}
all_locked={data.all_locked}
lock={data.locks && data.locks[emote.set_id]}
@@ -345,24 +421,34 @@ export default class EmoteMenu extends Module {
}
}
+ this.fine.wrap('ffz-menu-section', this.MenuSection);
+
+
this.MenuComponent = class FFZEmoteMenuComponent extends React.Component {
constructor(props) {
super(props);
- this.state = {page: null}
+ this.state = {tab: null}
this.componentWillReceiveProps(props);
this.clickTab = this.clickTab.bind(this);
this.clickRefresh = this.clickRefresh.bind(this);
this.handleFilterChange = this.handleFilterChange.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
+ }
+ componentDidMount() {
window.ffz_menu = this;
}
+ componentWillUnmount() {
+ if ( window.ffz_menu === this )
+ window.ffz_menu = null;
+ }
+
clickTab(event) {
this.setState({
- page: event.target.dataset.page
+ tab: event.target.dataset.tab
});
}
@@ -528,7 +614,8 @@ export default class EmoteMenu extends Module {
const set_id = parseInt(emote_set.id, 10),
set_data = data[set_id] || {},
more_data = t.emotes.getTwitchSetChannel(set_id),
- image = set_data.image;
+ image = set_data.image,
+ image_set = set_data.image_set;
set_ids.add(set_id);
@@ -585,6 +672,7 @@ export default class EmoteMenu extends Module {
if ( chan && ! chan.bad && section.bad ) {
section.title = title;
section.image = image;
+ section.image_set = image_set;
section.icon = icon;
section.sort_key = sort_key;
section.bad = false;
@@ -597,6 +685,7 @@ export default class EmoteMenu extends Module {
bad: chan ? chan.bad : true,
key,
image,
+ image_set,
icon,
title,
source: t.i18n.t('emote-menu.twitch', 'Twitch'),
@@ -640,6 +729,7 @@ export default class EmoteMenu extends Module {
sort_key: -10,
key: `twitch-${user.id}`,
image: badge && badge.image1x,
+ image_set: badge && `${badge.image1x} 1x, ${badge.image2x} 2x, ${badge.image4x} 4x`,
icon: 'twitch',
title: t.i18n.t('emote-menu.sub-set', 'Subscriber Emotes'),
source: t.i18n.t('emote-menu.twitch', 'Twitch'),
@@ -726,7 +816,7 @@ export default class EmoteMenu extends Module {
channel.sort(sort_sets);
all.sort(sort_sets);
- state.has_channel_page = channel.length > 0;
+ state.has_channel_tab = channel.length > 0;
return state;
}
@@ -795,7 +885,10 @@ export default class EmoteMenu extends Module {
return (
-

+
{t.i18n.t('emote-menu.error', 'There was an error rendering this menu.')}
@@ -810,7 +903,10 @@ export default class EmoteMenu extends Module {
renderEmpty() { // eslint-disable-line class-methods-use-this
return (
-

+
{this.state.filtered ?
t.i18n.t('emote-menu.empty-search', 'There are no matching emotes.') :
@@ -829,13 +925,14 @@ export default class EmoteMenu extends Module {
if ( ! this.props.visible )
return null;
- const loading = this.state.loading || this.props.loading;
+ const loading = this.state.loading || this.props.loading,
+ padding = t.chat.context.get('chat.emote-menu.reduced-padding');
- let page = this.state.page, sets;
- if ( ! page )
- page = this.state.has_channel_page ? 'channel' : 'all';
+ let tab = this.state.tab || t.chat.context.get('chat.emote-menu.default-tab'), sets;
+ if ( tab === 'channel' && ! this.state.has_channel_tab )
+ tab = 'all';
- switch(page) {
+ switch(tab) {
case 'channel':
sets = this.state.filtered_channel_sets;
break;
@@ -846,7 +943,7 @@ export default class EmoteMenu extends Module {
}
return (
@@ -869,7 +966,7 @@ export default class EmoteMenu extends Module {
-
+ {t.chat.context.get('chat.emote-menu.show-search') && (
+
)}
{null && (
)}
- {this.state.has_channel_page &&
{t.i18n.t('emote-menu.channel', 'Channel')}
}
{t.i18n.t('emote-menu.all', 'All')}
@@ -919,6 +1016,8 @@ export default class EmoteMenu extends Module {
);
}
}
+
+ this.fine.wrap('ffz-emote-menu', this.MenuComponent);
}
@@ -964,11 +1063,17 @@ export default class EmoteMenu extends Module {
const owner = product.owner || {},
badges = owner.broadcastBadges;
- let image;
+ let image, image_set;
if ( badges )
for(const badge of badges)
if ( badge.setID === 'subscriber' && badge.version === '0' ) {
image = badge.imageURL;
+
+ if ( image.endsWith('/1') ) {
+ const base = image.slice(0, -2);
+ image_set = `${base}/1 1x, ${base}/2 2x, ${base}/4 4x`;
+ }
+
break;
}
@@ -980,6 +1085,7 @@ export default class EmoteMenu extends Module {
set_id: parseInt(set_id, 10),
type: product.type,
image,
+ image_set,
user: {
id: owner.id,
login: owner.login,
diff --git a/src/sites/twitch-twilight/styles/chat.scss b/src/sites/twitch-twilight/styles/chat.scss
index c4887177..729ab916 100644
--- a/src/sites/twitch-twilight/styles/chat.scss
+++ b/src/sites/twitch-twilight/styles/chat.scss
@@ -45,6 +45,10 @@
animation: ffz-rotateplane 1.2s infinite linear;
}
+ .ffz--menu-badge {
+ width: 1.8rem; height: 1.8rem;
+ }
+
.ffz--expiry-info {
opacity: 0.5;
}
@@ -63,6 +67,10 @@
@media only screen and (max-height: 750px) {
.emote-picker__tab-content {
+ .twilight-root & {
+ max-height: calc(100vh - 31rem);
+ }
+
max-height: calc(100vh - 26rem);
}
}
@@ -108,4 +116,10 @@
}
}
+
+ &.reduced-padding {
+ .tw-pd-1 {
+ padding: 0.5rem !important;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/utilities/compat/fine.js b/src/utilities/compat/fine.js
index 0fa3f409..4726dbf5 100644
--- a/src/utilities/compat/fine.js
+++ b/src/utilities/compat/fine.js
@@ -299,6 +299,27 @@ export default class Fine extends Module {
}
+ wrap(key, cls) {
+ let wrapper;
+ if ( this._wrappers.has(key) )
+ wrapper = this._wrappers.get(key);
+ else {
+ wrapper = new FineWrapper(key, null, undefined, this);
+ this._wrappers.set(key, wrapper);
+ }
+
+ if ( cls ) {
+ if ( wrapper._class || wrapper.criteria )
+ throw new Error('tried setting a class on an already initialized FineWrapper');
+
+ wrapper._set(cls, new Set);
+ this._known_classes.set(cls, wrapper);
+ }
+
+ return wrapper;
+ }
+
+
_checkWaiters(nodes) {
if ( ! this._live_waiting )
return;