mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 05:15:54 +00:00
A ton of stuff. Initial migration to React 16. Initial changes for Rooms support. Add support for clip champ badge. Start using .gql source files that are compiled during import so we don't need to include GraphQL-tag in builds.
This commit is contained in:
parent
f0bcf7f53e
commit
0ae6e5021d
29 changed files with 536 additions and 263 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
<div class="list-header">4.0.0-beta1.5<span>@ef163f5d644217193110</span> <time datetime="2018-02-22">(2018-02-22)</time></div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: React 16 support.</li>
|
||||||
|
<li>Note: Many features are still broken. This is just an initial quick fix.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="list-header">4.0.0-beta1.5<span>@a752805865b1313466a7</span> <time datetime="2018-02-08">(2018-02-08)</time></div>
|
<div class="list-header">4.0.0-beta1.5<span>@a752805865b1313466a7</span> <time datetime="2018-02-08">(2018-02-08)</time></div>
|
||||||
<ul class="chat-menu-content menu-side-padding">
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
<li>Added: Setting to hide the Live indicator from live channels in the directory.</li>
|
<li>Added: Setting to hide the Live indicator from live channels in the directory.</li>
|
||||||
|
|
|
@ -18,11 +18,12 @@ export const CSS_BADGES = {
|
||||||
moderator: { 1: { color: '#34ae0a', svg: true } },
|
moderator: { 1: { color: '#34ae0a', svg: true } },
|
||||||
twitchbot: { 1: { color: '#34ae0a' } },
|
twitchbot: { 1: { color: '#34ae0a' } },
|
||||||
partner: { 1: { color: 'transparent', trans: { image: true, color: '#6441a5' } } },
|
partner: { 1: { color: 'transparent', trans: { image: true, color: '#6441a5' } } },
|
||||||
|
'clip-champ': { 1: { color: '#6441a5'} },
|
||||||
|
|
||||||
turbo: { 1: { color: '#6441a5', svg: true } },
|
turbo: { 1: { color: '#6441a5', svg: true } },
|
||||||
premium: { 1: { color: '#009cdc' } },
|
premium: { 1: { color: '#009cdc' } },
|
||||||
|
|
||||||
subscriber: { 0: { color: '#6441a4' }, 1: { color: '#6441a4' }},
|
subscriber: { 0: { color: '#6441a5' }, 1: { color: '#6441a5' }},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BADGE_POSITIONS = {
|
export const BADGE_POSITIONS = {
|
||||||
|
@ -143,13 +144,80 @@ export default class Badges extends Module {
|
||||||
|
|
||||||
this.style = new ManagedStyle('badges');
|
this.style = new ManagedStyle('badges');
|
||||||
this.badges = {};
|
this.badges = {};
|
||||||
|
this.twitch_badges = {};
|
||||||
|
|
||||||
this.twitch_badges = new Map;
|
this.settings.add('chat.badges.hidden', {
|
||||||
|
default: [],
|
||||||
|
_ui: {
|
||||||
|
path: 'Chat > Badges >> tabs ~> Visibility',
|
||||||
|
component: 'badge-visibility',
|
||||||
|
data: () => {
|
||||||
|
const twitch = [],
|
||||||
|
game = [],
|
||||||
|
ffz = [],
|
||||||
|
addon = [];
|
||||||
|
|
||||||
|
for(const key in this.twitch_badges)
|
||||||
|
if ( has(this.twitch_badges, key) ) {
|
||||||
|
const badge = this.twitch_badges[key],
|
||||||
|
vs = [];
|
||||||
|
let v = badge && (badge[1] || badge[0]);
|
||||||
|
|
||||||
|
for(const key in badge)
|
||||||
|
if ( has(badge, key) ) {
|
||||||
|
const version = badge[key];
|
||||||
|
if ( ! v )
|
||||||
|
v = version;
|
||||||
|
|
||||||
|
if ( version && version.image1x )
|
||||||
|
vs.push({
|
||||||
|
name: version.title,
|
||||||
|
image: version.image1x,
|
||||||
|
styleImage: `url("${version.image1x}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( v )
|
||||||
|
(badge.__game ? game : twitch).push({
|
||||||
|
id: key,
|
||||||
|
provider: 'twitch',
|
||||||
|
name: v.title,
|
||||||
|
color: 'transparent',
|
||||||
|
image: v.image4x,
|
||||||
|
versions: vs,
|
||||||
|
styleImage: `url("${v.image4x}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const key in this.badges)
|
||||||
|
if ( has(this.badges, key) ) {
|
||||||
|
const badge = this.badges[key],
|
||||||
|
image = badge.urls ? (badge.urls[4] || badge.urls[2] || badge.urls[1]) : badge.image;
|
||||||
|
|
||||||
|
(/^addon/.test(key) ? addon : ffz).push({
|
||||||
|
id: key,
|
||||||
|
provider: 'ffz',
|
||||||
|
name: badge.title,
|
||||||
|
color: badge.color || 'transparent',
|
||||||
|
image,
|
||||||
|
styleImage: `url("${image}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{title: 'Twitch', badges: twitch},
|
||||||
|
{title: 'Twitch: Game', badges: game},
|
||||||
|
{title: 'FrankerFaceZ', badges: ffz},
|
||||||
|
{title: 'Add-on', badges: addon}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.badges.style', {
|
this.settings.add('chat.badges.style', {
|
||||||
default: 0,
|
default: 0,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Chat > Badges >> Appearance',
|
path: 'Chat > Badges >> tabs ~> Appearance',
|
||||||
title: 'Style',
|
title: 'Style',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
|
@ -497,7 +565,7 @@ export default class Badges extends Module {
|
||||||
const b = {};
|
const b = {};
|
||||||
for(const data of badges) {
|
for(const data of badges) {
|
||||||
const sid = data.setID,
|
const sid = data.setID,
|
||||||
bs = b[sid] = b[sid] || {};
|
bs = b[sid] = b[sid] || {__game: /_\d+$/.test(sid)};
|
||||||
|
|
||||||
bs[data.version] = data;
|
bs[data.version] = data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,7 +429,15 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
|
|
||||||
tokenizeMessage(msg, user) {
|
tokenizeMessage(msg, user) {
|
||||||
|
if ( msg.content && ! msg.message )
|
||||||
|
msg.message = msg.content.text;
|
||||||
|
|
||||||
|
if ( msg.sender && ! msg.user )
|
||||||
|
msg.user = msg.sender;
|
||||||
|
|
||||||
let tokens = [{type: 'text', text: msg.message}];
|
let tokens = [{type: 'text', text: msg.message}];
|
||||||
|
if ( ! tokens[0].text )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
for(const tokenizer of this.__tokenizers)
|
for(const tokenizer of this.__tokenizers)
|
||||||
tokens = tokenizer.process.call(this, tokens, msg, user);
|
tokens = tokenizer.process.call(this, tokens, msg, user);
|
||||||
|
|
85
src/modules/main_menu/components/badge-visibility.vue
Normal file
85
src/modules/main_menu/components/badge-visibility.vue
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="ffz--badge-visibility tw-pd-t-05">
|
||||||
|
<section class="ffz--menu-container tw-border-t" v-for="sec in data">
|
||||||
|
<header>{{ sec.title }}</header>
|
||||||
|
<ul class="tw-flex tw-flex-wrap tw-align-content-start">
|
||||||
|
<li v-for="i in sort(sec.badges)" class="ffz--badge-info tw-pd-y-1 tw-pd-r-1 tw-flex" :class="{default: isDefault}">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="tw-checkbox__input"
|
||||||
|
checked="checked"
|
||||||
|
:id="i.id"
|
||||||
|
>
|
||||||
|
|
||||||
|
<label class="tw-checkbox__label tw-flex" :for="i.id">
|
||||||
|
<div class="preview-image ffz-badge tw-mg-r-1 tw-flex-shrink-0" :style="{backgroundColor: i.color, backgroundImage: i.styleImage }" />
|
||||||
|
<div>
|
||||||
|
<h5>{{ i.name }}</h5>
|
||||||
|
<section class="tw-mg-t-05" v-if="i.versions && i.versions.length > 1">
|
||||||
|
<span v-for="v in i.versions" data-tooltip-type="html" class="ffz-badge ffz-tooltip" :title="v.name" :style="{backgroundColor: i.color, backgroundImage: v.styleImage}" />
|
||||||
|
</section>
|
||||||
|
<!--button class="tw-mg-t-05 tw-button tw-button--hollow tw-tooltip-wrapper">
|
||||||
|
<span class="tw-button__text">Reset</span>
|
||||||
|
<span class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.reset', 'Reset to Default') }}
|
||||||
|
</span>
|
||||||
|
</button-->
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import SettingMixin from '../setting-mixin';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [SettingMixin],
|
||||||
|
props: ['item', 'context'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
sort(items) {
|
||||||
|
return items.sort((a, b) => {
|
||||||
|
const an = a.name.toLowerCase(),
|
||||||
|
bn = b.name.toLowerCase();
|
||||||
|
|
||||||
|
if ( an < bn ) return -1;
|
||||||
|
if ( an > bn ) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange() {
|
||||||
|
this.set(this.$refs.control.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ffz--badge-info {
|
||||||
|
&.default {
|
||||||
|
label:before, label:after {
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tw-checkbox__input:checked+.tw-checkbox__label:after,
|
||||||
|
label:before, label:after {
|
||||||
|
top: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-badge.preview-image {
|
||||||
|
width: 7.2rem;
|
||||||
|
height: 7.2rem;
|
||||||
|
background-size: 7.2rem;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 30rem;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -39,7 +39,7 @@ export default class TooltipProvider extends Module {
|
||||||
onEnable() {
|
onEnable() {
|
||||||
const container = document.body.querySelector('.twilight-root') || document.body;
|
const container = document.body.querySelector('.twilight-root') || document.body;
|
||||||
|
|
||||||
this.tips = new Tooltip('body [data-reactroot]', 'ffz-tooltip', {
|
this.tips = new Tooltip('body #root', 'ffz-tooltip', {
|
||||||
html: true,
|
html: true,
|
||||||
delayHide: this.checkDelayHide.bind(this),
|
delayHide: this.checkDelayHide.bind(this),
|
||||||
delayShow: this.checkDelayShow.bind(this),
|
delayShow: this.checkDelayShow.bind(this),
|
||||||
|
|
|
@ -38,10 +38,8 @@ export default class Twilight extends BaseSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
const root = this.fine.getParent(this.fine.react),
|
const thing = this.fine.searchTree(null, n => n.props && n.props.store),
|
||||||
ctx = this.context = root && root._context,
|
store = this.store = thing && thing.props && thing.props.store;
|
||||||
|
|
||||||
store = this.store = ctx && ctx.store;
|
|
||||||
|
|
||||||
if ( ! store )
|
if ( ! store )
|
||||||
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
||||||
|
@ -64,10 +62,10 @@ export default class Twilight extends BaseSite {
|
||||||
updateContext() {
|
updateContext() {
|
||||||
try {
|
try {
|
||||||
const state = this.store.getState(),
|
const state = this.store.getState(),
|
||||||
route = this.context.router && this.context.router.route;
|
history = this.router && this.router.history;
|
||||||
|
|
||||||
this.settings.updateContext({
|
this.settings.updateContext({
|
||||||
location: route && route.location,
|
location: history && history.location,
|
||||||
ui: state && state.ui,
|
ui: state && state.ui,
|
||||||
session: state && state.session
|
session: state && state.session
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {deep_copy} from 'utilities/object';
|
import {deep_copy} from 'utilities/object';
|
||||||
|
|
||||||
|
import CHANNEL_QUERY from './channel_bar_query.gql';
|
||||||
|
|
||||||
|
|
||||||
export default class ChannelBar extends Module {
|
export default class ChannelBar extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -17,17 +20,9 @@ export default class ChannelBar extends Module {
|
||||||
this.inject('site.apollo');
|
this.inject('site.apollo');
|
||||||
this.inject('metadata');
|
this.inject('metadata');
|
||||||
|
|
||||||
|
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', CHANNEL_QUERY);
|
||||||
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', this.apollo.gql`query {
|
|
||||||
user {
|
|
||||||
stream {
|
|
||||||
createdAt
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', data => {
|
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', data => {
|
||||||
|
console.log('BOOP BOOP A DOOP', data);
|
||||||
const u = data && data.data && data.data.user;
|
const u = data && data.data && data.data.user;
|
||||||
if ( u ) {
|
if ( u ) {
|
||||||
const o = u.profileViewCount = new Number(u.profileViewCount || 0);
|
const o = u.profileViewCount = new Number(u.profileViewCount || 0);
|
||||||
|
|
8
src/sites/twitch-twilight/modules/channel_bar_query.gql
Normal file
8
src/sites/twitch-twilight/modules/channel_bar_query.gql
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
query {
|
||||||
|
user {
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,25 +61,30 @@ const EVENTS = [
|
||||||
'onJoinedEvent',
|
'onJoinedEvent',
|
||||||
'onDisconnectedEvent',
|
'onDisconnectedEvent',
|
||||||
'onReconnectingEvent',
|
'onReconnectingEvent',
|
||||||
|
'onChatMessageEvent',
|
||||||
|
'onChatNoticeEvent',
|
||||||
|
'onChatActionEvent',
|
||||||
|
'onBadgesUpdatedEvent',
|
||||||
'onHostingEvent',
|
'onHostingEvent',
|
||||||
'onUnhostEvent',
|
'onUnhostEvent',
|
||||||
'onChatMessageEvent',
|
'onPurchaseEvent',
|
||||||
'onChatActionEvent',
|
'onCrateEvent',
|
||||||
'onChatNoticeEvent',
|
//'onRitualEvent',
|
||||||
'onTimeoutEvent',
|
|
||||||
'onBanEvent',
|
|
||||||
'onModerationEvent',
|
|
||||||
'onSubscriptionEvent',
|
'onSubscriptionEvent',
|
||||||
//'onResubscriptionEvent',
|
//'onResubscriptionEvent',
|
||||||
'onSubscriptionGiftEvent',
|
'onSubscriptionGiftEvent',
|
||||||
'onRoomStateEvent',
|
'onTimeoutEvent',
|
||||||
'onSlowModeEvent',
|
'onBanEvent',
|
||||||
'onFollowerOnlyModeEvent',
|
|
||||||
'onSubscriberOnlyModeEvent',
|
|
||||||
'onClearChatEvent',
|
'onClearChatEvent',
|
||||||
'onRaidEvent',
|
'onRaidEvent',
|
||||||
'onUnraidEvent',
|
'onUnraidEvent',
|
||||||
'onBadgesUpdatedEvent'
|
'onRoomModsEvent',
|
||||||
|
'onRoomStateEvent',
|
||||||
|
'onFollowerOnlyModeEvent',
|
||||||
|
'onSlowModeEvent',
|
||||||
|
'onSubscriberOnlyModeEvent',
|
||||||
|
'onEmoteOnlyModeEvent',
|
||||||
|
'onBitsCharityEvent'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,7 +413,7 @@ export default class ChatHook extends Module {
|
||||||
const i = this,
|
const i = this,
|
||||||
pm = this.postMessage;
|
pm = this.postMessage;
|
||||||
|
|
||||||
for(const key of EVENTS) { // eslint-disable-line guard-for-in
|
for(const key of EVENTS) {
|
||||||
const original = this[key];
|
const original = this[key];
|
||||||
if ( original )
|
if ( original )
|
||||||
this[key] = function(e, t) {
|
this[key] = function(e, t) {
|
||||||
|
|
|
@ -25,7 +25,12 @@ export default class ChatLine extends Module {
|
||||||
|
|
||||||
this.ChatLine = this.fine.define(
|
this.ChatLine = this.fine.define(
|
||||||
'chat-line',
|
'chat-line',
|
||||||
n => n.renderMessageBody
|
n => n.renderMessageBody && ! n.getMessageParts
|
||||||
|
);
|
||||||
|
|
||||||
|
this.ChatRoomLine = this.fine.define(
|
||||||
|
'chat-room-line',
|
||||||
|
n => n.renderMessageBody && n.getMessageParts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +69,23 @@ export default class ChatLine extends Module {
|
||||||
const types = t.parent.chat_types || {},
|
const types = t.parent.chat_types || {},
|
||||||
|
|
||||||
msg = this.props.message,
|
msg = this.props.message,
|
||||||
is_action = msg.type === types.Action,
|
is_action = msg.type === types.Action;
|
||||||
user = msg.user,
|
|
||||||
|
if ( msg.content && ! msg.message )
|
||||||
|
msg.message = msg.content.text;
|
||||||
|
|
||||||
|
if ( msg.sender && ! msg.user ) {
|
||||||
|
msg.user = msg.sender;
|
||||||
|
msg.user.color = msg.user.color || msg.user.chatColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! msg.badges && msg.user.displayBadges ) {
|
||||||
|
const b = msg.badges = {};
|
||||||
|
for(const item of msg.user.displayBadges)
|
||||||
|
b[item.setID] = item.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = msg.user,
|
||||||
color = t.parent.colors.process(user.color),
|
color = t.parent.colors.process(user.color),
|
||||||
/*bg_rgb = Color.RGBA.fromHex(user.color),
|
/*bg_rgb = Color.RGBA.fromHex(user.color),
|
||||||
bg_color = bg_rgb.luminance() < .005 ? bg_rgb : bg_rgb.toHSLA().targetLuminance(0.005).toRGBA(),
|
bg_color = bg_rgb.luminance() < .005 ? bg_rgb : bg_rgb.toHSLA().targetLuminance(0.005).toRGBA(),
|
||||||
|
|
72
src/sites/twitch-twilight/modules/compat_emote_menu.js
Normal file
72
src/sites/twitch-twilight/modules/compat_emote_menu.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Compatibility Layer
|
||||||
|
// Emote Menu for Twitch (BTTV Emote Menu)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
import Module from 'utilities/module';
|
||||||
|
import {has} from 'utilities/object';
|
||||||
|
|
||||||
|
export default class CompatEmoteMenu extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.should_enable = true;
|
||||||
|
|
||||||
|
this.inject('site.chat');
|
||||||
|
this.inject('chat.emotes');
|
||||||
|
}
|
||||||
|
|
||||||
|
async onEnable() {
|
||||||
|
const em = await this.findEmoteMenu();
|
||||||
|
if ( ! em )
|
||||||
|
return this.log.info('Emote Menu for Twitch was not found after 60 seconds.');
|
||||||
|
|
||||||
|
em.registerEmoteGetter('FrankerFaceZ', () => {
|
||||||
|
// We get literally no information about the current context,
|
||||||
|
// so we need to look up everything.
|
||||||
|
const cont = this.chat.ChatContainer.first,
|
||||||
|
props = cont && cont.props;
|
||||||
|
|
||||||
|
if ( ! props )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const sets = this.emotes.getSets(props.userID, props.currentUserLogin, props.channelID, props.channelLogin),
|
||||||
|
emotes = [];
|
||||||
|
|
||||||
|
for(const set of sets) {
|
||||||
|
if ( ! set || ! set.emotes )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(const emote_id in set.emotes)
|
||||||
|
if ( has(set.emotes, emote_id) ) {
|
||||||
|
const emote = set.emotes[emote_id];
|
||||||
|
if ( emote.hidden )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
emotes.push({
|
||||||
|
text: emote.name,
|
||||||
|
url: emote.urls[1],
|
||||||
|
channel: `${set.source || 'FrankerFaceZ'} ${set.title}`,
|
||||||
|
badge: set.icon || '//cdn.frankerfacez.com/script/devicon.png'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emotes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findEmoteMenu(delay = 0) {
|
||||||
|
if ( window.emoteMenu && emoteMenu.registerEmoteGetter )
|
||||||
|
return emoteMenu;
|
||||||
|
|
||||||
|
if ( delay >= 60000 )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Promise(s => {
|
||||||
|
setTimeout(() => this.findEmoteMenu(delay + 100).then(s), 100)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
query {
|
||||||
|
streams {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
createdAt
|
||||||
|
broadcaster {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@
|
||||||
import {SiteModule} from 'utilities/module';
|
import {SiteModule} from 'utilities/module';
|
||||||
import {get} from 'utilities/object';
|
import {get} from 'utilities/object';
|
||||||
|
|
||||||
|
import BROWSE_POPULAR from './browse_popular.gql';
|
||||||
|
|
||||||
export default class BrowsePopular extends SiteModule {
|
export default class BrowsePopular extends SiteModule {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -15,18 +17,7 @@ export default class BrowsePopular extends SiteModule {
|
||||||
this.inject('site.fine');
|
this.inject('site.fine');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
|
||||||
this.apollo.registerModifier('BrowsePage_Popular', this.apollo.gql`query {
|
this.apollo.registerModifier('BrowsePage_Popular', BROWSE_POPULAR);
|
||||||
streams {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
createdAt
|
|
||||||
broadcaster {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.ChannelCard = this.fine.define(
|
this.ChannelCard = this.fine.define(
|
||||||
'browse-all-channel-card',
|
'browse-all-channel-card',
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Directory (Following, for now)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
import {SiteModule} from 'utilities/module';
|
|
||||||
|
|
||||||
export default class Community extends SiteModule {
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
|
|
||||||
this.inject('site.apollo');
|
|
||||||
|
|
||||||
this.apollo.registerModifier('GamePage_Game', this.apollo.gql`query {
|
|
||||||
directory {
|
|
||||||
... on Community {
|
|
||||||
streams {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
createdAt
|
|
||||||
type
|
|
||||||
broadcaster {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
query {
|
||||||
|
currentUser {
|
||||||
|
followedLiveUsers {
|
||||||
|
nodes {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
stream {
|
||||||
|
type
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
query {
|
||||||
|
currentUser {
|
||||||
|
followedHosts {
|
||||||
|
nodes {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
hosting {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
query {
|
||||||
|
currentUser {
|
||||||
|
followedLiveUsers {
|
||||||
|
nodes {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
followedHosts {
|
||||||
|
nodes {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
hosting {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
query {
|
||||||
|
currentUser {
|
||||||
|
followedLiveUsers {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,11 @@ import {get} from 'utilities/object';
|
||||||
|
|
||||||
import Popper from 'popper.js';
|
import Popper from 'popper.js';
|
||||||
|
|
||||||
|
import FOLLOWED_INDEX from './followed_index.gql';
|
||||||
|
import FOLLOWED_HOSTS from './followed_hosts.gql';
|
||||||
|
import FOLLOWED_CHANNELS from './followed_channels.gql';
|
||||||
|
import FOLLOWED_LIVE from './followed_live.gql';
|
||||||
|
|
||||||
export default class Following extends SiteModule {
|
export default class Following extends SiteModule {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -58,75 +63,10 @@ export default class Following extends SiteModule {
|
||||||
changed: () => this.ChannelCard.forceUpdate()
|
changed: () => this.ChannelCard.forceUpdate()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.apollo.registerModifier('FollowedIndex_CurrentUser', this.apollo.gql`query {
|
this.apollo.registerModifier('FollowedIndex_CurrentUser', FOLLOWED_INDEX);
|
||||||
currentUser {
|
this.apollo.registerModifier('FollowingLive_CurrentUser', FOLLOWED_LIVE);
|
||||||
followedLiveUsers {
|
this.apollo.registerModifier('FollowingHosts_CurrentUser', FOLLOWED_HOSTS);
|
||||||
nodes {
|
this.apollo.registerModifier('FollowedChannels', FOLLOWED_CHANNELS);
|
||||||
profileImageURL(width: 70)
|
|
||||||
stream {
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
followedHosts {
|
|
||||||
nodes {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
hosting {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
stream {
|
|
||||||
createdAt
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.apollo.registerModifier('FollowingLive_CurrentUser', this.apollo.gql`query {
|
|
||||||
currentUser {
|
|
||||||
followedLiveUsers {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
stream {
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.apollo.registerModifier('FollowingHosts_CurrentUser', this.apollo.gql`query {
|
|
||||||
currentUser {
|
|
||||||
followedHosts {
|
|
||||||
nodes {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
hosting {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
stream {
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.apollo.registerModifier('FollowedChannels', this.apollo.gql`query {
|
|
||||||
currentUser {
|
|
||||||
followedLiveUsers {
|
|
||||||
nodes {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
stream {
|
|
||||||
type
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
|
|
||||||
this.ChannelCard = this.fine.define(
|
this.ChannelCard = this.fine.define(
|
||||||
'following-channel-card',
|
'following-channel-card',
|
||||||
|
|
30
src/sites/twitch-twilight/modules/directory/game.gql
Normal file
30
src/sites/twitch-twilight/modules/directory/game.gql
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
query {
|
||||||
|
directory {
|
||||||
|
... on Game {
|
||||||
|
streams {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
createdAt
|
||||||
|
type
|
||||||
|
broadcaster {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on Community {
|
||||||
|
streams {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
createdAt
|
||||||
|
type
|
||||||
|
broadcaster {
|
||||||
|
profileImageURL(width: 70)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@
|
||||||
import {SiteModule} from 'utilities/module';
|
import {SiteModule} from 'utilities/module';
|
||||||
import {createElement as e} from 'utilities/dom';
|
import {createElement as e} from 'utilities/dom';
|
||||||
|
|
||||||
|
import GAME_QUERY from './game.gql';
|
||||||
|
|
||||||
export default class Game extends SiteModule {
|
export default class Game extends SiteModule {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -22,23 +24,7 @@ export default class Game extends SiteModule {
|
||||||
n => n.renderFollowButton && n.renderGameDetailsTab
|
n => n.renderFollowButton && n.renderGameDetailsTab
|
||||||
);
|
);
|
||||||
|
|
||||||
this.apollo.registerModifier('GamePage_Game', this.apollo.gql`query {
|
this.apollo.registerModifier('GamePage_Game', GAME_QUERY);
|
||||||
directory {
|
|
||||||
... on Game {
|
|
||||||
streams {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
createdAt
|
|
||||||
type
|
|
||||||
broadcaster {
|
|
||||||
profileImageURL(width: 70)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {get} from 'utilities/object';
|
||||||
|
|
||||||
import Following from './following';
|
import Following from './following';
|
||||||
import Game from './game';
|
import Game from './game';
|
||||||
import Community from './community';
|
|
||||||
import BrowsePopular from './browse_popular';
|
import BrowsePopular from './browse_popular';
|
||||||
|
|
||||||
export default class Directory extends SiteModule {
|
export default class Directory extends SiteModule {
|
||||||
|
@ -30,7 +29,6 @@ export default class Directory extends SiteModule {
|
||||||
|
|
||||||
this.inject(Following);
|
this.inject(Following);
|
||||||
this.inject(Game);
|
this.inject(Game);
|
||||||
this.inject(Community);
|
|
||||||
this.inject(BrowsePopular);
|
this.inject(BrowsePopular);
|
||||||
|
|
||||||
this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false);
|
this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false);
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default class HostButton extends Module {
|
||||||
|
|
||||||
if (userLogin)
|
if (userLogin)
|
||||||
this.joinChannel(userLogin);
|
this.joinChannel(userLogin);
|
||||||
|
|
||||||
this.metadata.updateMetadata('host');
|
this.metadata.updateMetadata('host');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -57,9 +57,9 @@ export default class HostButton extends Module {
|
||||||
button: true,
|
button: true,
|
||||||
|
|
||||||
disabled: () => {
|
disabled: () => {
|
||||||
return this._host_updating || this._host_error;
|
return this._host_updating || this._host_error;
|
||||||
},
|
},
|
||||||
|
|
||||||
click: data => {
|
click: data => {
|
||||||
if (data.channel) this.sendHostUnhostCommand(data.channel.login);
|
if (data.channel) this.sendHostUnhostCommand(data.channel.login);
|
||||||
},
|
},
|
||||||
|
@ -69,7 +69,7 @@ export default class HostButton extends Module {
|
||||||
_host_options_vue = import(/* webpackChunkName: "host-options" */ './host-options.vue'),
|
_host_options_vue = import(/* webpackChunkName: "host-options" */ './host-options.vue'),
|
||||||
_autoHosts = this.fetchAutoHosts(),
|
_autoHosts = this.fetchAutoHosts(),
|
||||||
_autoHostSettings = this.fetchAutoHostSettings();
|
_autoHostSettings = this.fetchAutoHostSettings();
|
||||||
|
|
||||||
const [, host_options_vue, autoHosts, autoHostSettings] = await Promise.all([vue.enable(), _host_options_vue, _autoHosts, _autoHostSettings]);
|
const [, host_options_vue, autoHosts, autoHostSettings] = await Promise.all([vue.enable(), _host_options_vue, _autoHosts, _autoHostSettings]);
|
||||||
|
|
||||||
this._auto_host_tip = tip;
|
this._auto_host_tip = tip;
|
||||||
|
@ -86,7 +86,7 @@ export default class HostButton extends Module {
|
||||||
|
|
||||||
const ffz_user = this.site.getUser(),
|
const ffz_user = this.site.getUser(),
|
||||||
userLogin = ffz_user && ffz_user.login;
|
userLogin = ffz_user && ffz_user.login;
|
||||||
|
|
||||||
if (data.channel && data.channel.login === userLogin) {
|
if (data.channel && data.channel.login === userLogin) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -168,22 +168,22 @@ export default class HostButton extends Module {
|
||||||
|
|
||||||
this.on('tmi:host', e => {
|
this.on('tmi:host', e => {
|
||||||
if (e.channel.substring(1) !== userLogin) return;
|
if (e.channel.substring(1) !== userLogin) return;
|
||||||
|
|
||||||
clearTimeout(this._host_feedback);
|
clearTimeout(this._host_feedback);
|
||||||
this._host_error = false;
|
this._host_error = false;
|
||||||
this._last_hosted_channel = e.target;
|
this._last_hosted_channel = e.target;
|
||||||
|
|
||||||
this._host_updating = false;
|
this._host_updating = false;
|
||||||
this.metadata.updateMetadata('host');
|
this.metadata.updateMetadata('host');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('tmi:unhost', e => {
|
this.on('tmi:unhost', e => {
|
||||||
if (e.channel.substring(1) !== userLogin) return;
|
if (e.channel.substring(1) !== userLogin) return;
|
||||||
|
|
||||||
clearTimeout(this._host_feedback);
|
clearTimeout(this._host_feedback);
|
||||||
this._host_error = false;
|
this._host_error = false;
|
||||||
this._last_hosted_channel = null;
|
this._last_hosted_channel = null;
|
||||||
|
|
||||||
this._host_updating = false;
|
this._host_updating = false;
|
||||||
this.metadata.updateMetadata('host');
|
this.metadata.updateMetadata('host');
|
||||||
});
|
});
|
||||||
|
@ -232,12 +232,12 @@ export default class HostButton extends Module {
|
||||||
const t = e.target,
|
const t = e.target,
|
||||||
setting = t.dataset.setting;
|
setting = t.dataset.setting;
|
||||||
let state = t.checked;
|
let state = t.checked;
|
||||||
|
|
||||||
if ( setting === 'strategy' )
|
if ( setting === 'strategy' )
|
||||||
state = state ? 'random' : 'ordered';
|
state = state ? 'random' : 'ordered';
|
||||||
else if ( setting === 'deprioritize_vodcast' )
|
else if ( setting === 'deprioritize_vodcast' )
|
||||||
state = ! state;
|
state = ! state;
|
||||||
|
|
||||||
this.updateAutoHostSetting(setting, state);
|
this.updateAutoHostSetting(setting, state);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -302,20 +302,20 @@ export default class HostButton extends Module {
|
||||||
|
|
||||||
queueHostUpdate() {
|
queueHostUpdate() {
|
||||||
if (this._host_update_timer) clearTimeout(this._host_update_timer);
|
if (this._host_update_timer) clearTimeout(this._host_update_timer);
|
||||||
|
|
||||||
this._host_update_timer = setTimeout(() => {
|
this._host_update_timer = setTimeout(() => {
|
||||||
this._host_update_timer = undefined;
|
this._host_update_timer = undefined;
|
||||||
this.updateAutoHosts(this.autoHosts);
|
this.updateAutoHosts(this.autoHosts);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
rearrangeHosts(oldIndex, newIndex) {
|
rearrangeHosts(oldIndex, newIndex) {
|
||||||
const host = this.autoHosts.splice(oldIndex, 1)[0];
|
const host = this.autoHosts.splice(oldIndex, 1)[0];
|
||||||
this.autoHosts.splice(newIndex, 0, host);
|
this.autoHosts.splice(newIndex, 0, host);
|
||||||
|
|
||||||
this.queueHostUpdate();
|
this.queueHostUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRoomInHosts() {
|
currentRoomInHosts() {
|
||||||
return this.getAutoHostIDs(this.autoHosts).includes(parseInt(this._current_channel_id, 10));
|
return this.getAutoHostIDs(this.autoHosts).includes(parseInt(this._current_channel_id, 10));
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ export default class HostButton extends Module {
|
||||||
|
|
||||||
removeUserFromHosts(event) {
|
removeUserFromHosts(event) {
|
||||||
const id = event.target.closest('.ffz--host-user').dataset.id;
|
const id = event.target.closest('.ffz--host-user').dataset.id;
|
||||||
|
|
||||||
const newHosts = [];
|
const newHosts = [];
|
||||||
for (let i = 0; i < this.autoHosts.length; i++) {
|
for (let i = 0; i < this.autoHosts.length; i++) {
|
||||||
if (this.autoHosts[i]._id != id) newHosts.push(this.autoHosts[i]);
|
if (this.autoHosts[i]._id != id) newHosts.push(this.autoHosts[i]);
|
||||||
|
|
|
@ -192,12 +192,12 @@ export default class Player extends Module {
|
||||||
|
|
||||||
disableAutoplay(inst) {
|
disableAutoplay(inst) {
|
||||||
if ( ! inst.player ) {
|
if ( ! inst.player ) {
|
||||||
this.log.warn("disableAutoplay() called but Player was not ready");
|
this.log.warn('disableAutoplay() called but Player was not ready');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! inst.ffzAutoplay ) {
|
if ( ! inst.ffzAutoplay ) {
|
||||||
var playListener = () => {
|
const playListener = () => {
|
||||||
this.log.info('Auto-paused player');
|
this.log.info('Auto-paused player');
|
||||||
inst.ffzAutoplay = null;
|
inst.ffzAutoplay = null;
|
||||||
inst.player.pause();
|
inst.player.pause();
|
||||||
|
@ -209,6 +209,7 @@ export default class Player extends Module {
|
||||||
inst.player.removeEventListener('contentShowing', playListener);
|
inst.player.removeEventListener('contentShowing', playListener);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
inst.ffzAutoplay = playListener;
|
inst.ffzAutoplay = playListener;
|
||||||
inst.player.addEventListener('play', inst.ffzAutoplay);
|
inst.player.addEventListener('play', inst.ffzAutoplay);
|
||||||
inst.player.addEventListener('playing', inst.ffzAutoplay);
|
inst.player.addEventListener('playing', inst.ffzAutoplay);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {has, get} from 'utilities/object';
|
import {has, get} from 'utilities/object';
|
||||||
import gql from 'graphql-tag';
|
|
||||||
|
|
||||||
export default class Apollo extends Module {
|
export default class Apollo extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
|
@ -16,17 +15,8 @@ export default class Apollo extends Module {
|
||||||
this.modifiers = {};
|
this.modifiers = {};
|
||||||
this.post_modifiers = {};
|
this.post_modifiers = {};
|
||||||
|
|
||||||
this.gql = gql;
|
|
||||||
|
|
||||||
this.inject('..web_munch');
|
this.inject('..web_munch');
|
||||||
this.inject('..fine');
|
this.inject('..fine');
|
||||||
|
|
||||||
this.registerModifier('ViewerCard', gql`query {
|
|
||||||
targetUser: user {
|
|
||||||
createdAt
|
|
||||||
profileViewCount
|
|
||||||
}
|
|
||||||
}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
|
@ -34,10 +24,10 @@ export default class Apollo extends Module {
|
||||||
let client = this.client;
|
let client = this.client;
|
||||||
|
|
||||||
if ( ! client ) {
|
if ( ! client ) {
|
||||||
const root = this.fine.getParent(this.fine.react),
|
const root = this.fine.react,
|
||||||
ctx = root && root._context;
|
inst = root && root.stateNode;
|
||||||
|
|
||||||
client = this.client = ctx && ctx.client;
|
client = this.client = inst && inst.props && inst.props.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.printer = this.web_munch.getModule('gql-printer');
|
this.printer = this.web_munch.getModule('gql-printer');
|
||||||
|
|
|
@ -22,10 +22,8 @@ export default class FineRouter extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
const root = this.fine.getParent(this.fine.react),
|
const thing = this.fine.searchTree(null, n => n.props && n.props.history),
|
||||||
ctx = this.context = root && root._context,
|
history = this.history = thing && thing.props && thing.props.history;
|
||||||
router = ctx && ctx.router,
|
|
||||||
history = this.history = router && router.history;
|
|
||||||
|
|
||||||
if ( ! history )
|
if ( ! history )
|
||||||
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
||||||
|
|
|
@ -24,20 +24,21 @@ export default class Fine extends Module {
|
||||||
async onEnable(tries=0) {
|
async onEnable(tries=0) {
|
||||||
// TODO: Move awaitElement to utilities/dom
|
// TODO: Move awaitElement to utilities/dom
|
||||||
if ( ! this.root_element )
|
if ( ! this.root_element )
|
||||||
this.root_element = await this.parent.awaitElement(this.selector || 'body [data-reactroot]');
|
this.root_element = await this.parent.awaitElement(this.selector || 'body #root');
|
||||||
|
|
||||||
const accessor = this.accessor = Fine.findAccessor(this.root_element);
|
if ( ! this.root_element || ! this.root_element._reactRootContainer ) {
|
||||||
if ( ! accessor ) {
|
|
||||||
if ( tries > 500 )
|
if ( tries > 500 )
|
||||||
throw new Error(`unable to find React after 25 seconds`);
|
throw new Error('Unable to find React after 25 seconds');
|
||||||
|
this.root_element = null;
|
||||||
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable(tries+1));
|
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable(tries+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.react = this.getReactInstance(this.root_element);
|
this.react_root = this.root_element._reactRootContainer;
|
||||||
|
this.react = this.react_root.current.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDisable() {
|
onDisable() {
|
||||||
this.root_element = this.react = this.accessor = null;
|
this.react_root = this.root_element = this.react = this.accessor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,101 +54,128 @@ export default class Fine extends Module {
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
getReactInstance(element) {
|
getReactInstance(element) {
|
||||||
|
if ( ! this.accessor )
|
||||||
|
this.accessor = Fine.findAccessor(element);
|
||||||
|
if ( ! this.accessor )
|
||||||
|
return;
|
||||||
|
|
||||||
return element[this.accessor];
|
return element[this.accessor];
|
||||||
}
|
}
|
||||||
|
|
||||||
getOwner(instance) {
|
getOwner(instance) {
|
||||||
if ( instance._reactInternalInstance )
|
if ( instance._reactInternalFiber )
|
||||||
instance = instance._reactInternalInstance;
|
instance = instance._reactInternalFiber;
|
||||||
else if ( instance instanceof Node )
|
else if ( instance instanceof Node )
|
||||||
instance = this.getReactInstance(instance);
|
instance = this.getReactInstance(instance);
|
||||||
|
|
||||||
if ( ! instance )
|
if ( ! instance )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return instance._owner || (instance._currentElement && instance._currentElement._owner);
|
return instance.return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHostNode(instance) { //eslint-disable-line class-methods-use-this
|
getHostNode(instance) {
|
||||||
if ( instance._reactInternalInstance )
|
if ( instance._reactInternalFiber )
|
||||||
instance = instance._reactInternalInstance;
|
instance = instance._reactInternalFiber;
|
||||||
else if ( instance instanceof Node )
|
else if ( instance instanceof Node )
|
||||||
instance = this.getReactInstance(instance);
|
instance = this.getReactInstance(instance);
|
||||||
|
|
||||||
while( instance )
|
while( instance )
|
||||||
if ( instance._hostNode )
|
if ( instance.stateNode instanceof Node )
|
||||||
return instance._hostNode;
|
return instance.stateNode
|
||||||
else
|
else
|
||||||
instance = instance._renderedComponent;
|
instance = instance.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
getParent(instance) {
|
getParent(instance) {
|
||||||
const owner = this.getOwner(instance);
|
return this.getOwner(instance);
|
||||||
return owner && this.getOwner(owner);
|
}
|
||||||
|
|
||||||
|
getFirstChild(node) {
|
||||||
|
if ( node._reactInternalFiber )
|
||||||
|
node = node._reactInternalFiber;
|
||||||
|
else if ( node instanceof Node )
|
||||||
|
node = this.getReactInstance(node);
|
||||||
|
|
||||||
|
if ( ! node )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return node.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren(node) {
|
||||||
|
if ( node._reactInternalFiber )
|
||||||
|
node = node._reactInternalFiber;
|
||||||
|
else if ( node instanceof Node )
|
||||||
|
node = this.getReactInstance(node);
|
||||||
|
|
||||||
|
if ( ! node )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const children = [];
|
||||||
|
let child = node.child;
|
||||||
|
while(child) {
|
||||||
|
children.push(child);
|
||||||
|
child = child.sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchParent(node, criteria, max_depth=15, depth=0) {
|
searchParent(node, criteria, max_depth=15, depth=0) {
|
||||||
if ( node._reactInternalInstance )
|
if ( node._reactInternalFiber )
|
||||||
node = node._reactInternalInstance;
|
node = node._reactInternalFiber;
|
||||||
else if ( node instanceof Node )
|
else if ( node instanceof Node )
|
||||||
node = this.getReactInstance(node);
|
node = this.getReactInstance(node);
|
||||||
|
|
||||||
if ( ! node || depth > max_depth )
|
if ( ! node || depth > max_depth )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const inst = node._instance;
|
const inst = node.stateNode;
|
||||||
if ( inst && criteria(inst) )
|
if ( inst && criteria(inst) )
|
||||||
return inst;
|
return inst;
|
||||||
|
|
||||||
if ( node._currentElement && node._currentElement._owner ) {
|
if ( node.return ) {
|
||||||
const result = this.searchParent(node._currentElement._owner, criteria, max_depth, depth+1);
|
const result = this.searchParent(node.return, criteria, max_depth, depth+1);
|
||||||
if ( result )
|
if ( result )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( node._hostParent )
|
|
||||||
return this.searchParent(node._hostParent, criteria, max_depth, depth+1);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchTree(node, criteria, max_depth=15, depth=0) {
|
searchTree(node, criteria, max_depth=15, depth=0) {
|
||||||
if ( ! node )
|
if ( ! node )
|
||||||
node = this.react;
|
node = this.react;
|
||||||
else if ( node._reactInternalInstance )
|
else if ( node._reactInternalFiber )
|
||||||
node = node._reactInternalInstance;
|
node = node._reactInternalFiber;
|
||||||
else if ( node instanceof Node )
|
else if ( node instanceof Node )
|
||||||
node = this.getReactInstance(node);
|
node = this.getReactInstance(node);
|
||||||
|
|
||||||
if ( ! node || depth > max_depth )
|
if ( ! node || depth > max_depth )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const inst = node._instance;
|
const inst = node.stateNode;
|
||||||
if ( inst && criteria(inst) )
|
if ( inst && criteria(inst) )
|
||||||
return inst;
|
return inst;
|
||||||
|
|
||||||
const children = node._renderedChildren,
|
if ( node.child ) {
|
||||||
component = node._renderedComponent;
|
let child = node.child;
|
||||||
|
while(child) {
|
||||||
if ( children )
|
const result = this.searchTree(child, criteria, max_depth, depth+1);
|
||||||
for(const key in children)
|
if ( result )
|
||||||
if ( has(children, key) ) {
|
return result;
|
||||||
const child = children[key];
|
child = child.sibling;
|
||||||
const result = child && this.searchTree(child, criteria, max_depth, depth+1);
|
}
|
||||||
if ( result )
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( component )
|
|
||||||
return this.searchTree(component, criteria, max_depth, depth+1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
searchAll(node, criterias, max_depth=15, depth=0, data) {
|
searchAll(node, criterias, max_depth=15, depth=0, data) {
|
||||||
if ( ! node )
|
if ( ! node )
|
||||||
node = this.react;
|
node = this.react;
|
||||||
else if ( node._reactInternalInstance )
|
else if ( node._reactInternalFiber )
|
||||||
node = node._reactInternalInstance;
|
node = node._reactInternalFiber;
|
||||||
else if ( node instanceof Node )
|
else if ( node instanceof Node )
|
||||||
node = this.getReactInstance(node);
|
node = this.getReactInstance(node);
|
||||||
|
|
||||||
|
@ -167,7 +195,7 @@ export default class Fine extends Module {
|
||||||
if ( depth > data.max_depth )
|
if ( depth > data.max_depth )
|
||||||
data.max_depth = depth;
|
data.max_depth = depth;
|
||||||
|
|
||||||
const inst = node._instance;
|
const inst = node.stateNode;
|
||||||
if ( inst ) {
|
if ( inst ) {
|
||||||
const cls = inst.constructor,
|
const cls = inst.constructor,
|
||||||
idx = data.classes.indexOf(cls);
|
idx = data.classes.indexOf(cls);
|
||||||
|
@ -189,18 +217,13 @@ export default class Fine extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = node._renderedChildren,
|
if ( node.child ) {
|
||||||
component = node._renderedComponent;
|
let child = node.child;
|
||||||
|
while(child) {
|
||||||
if ( children )
|
this.searchAll(child, criterias, max_depth, depth+1, data);
|
||||||
for(const key in children)
|
child = child.sibling;
|
||||||
if ( has(children, key) ) {
|
}
|
||||||
const child = children[key];
|
}
|
||||||
child && this.searchAll(child, criterias, max_depth, depth+1, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( component )
|
|
||||||
this.searchAll(component, criterias, max_depth, depth+1, data);
|
|
||||||
|
|
||||||
return data.out;
|
return data.out;
|
||||||
}
|
}
|
||||||
|
@ -400,8 +423,9 @@ export class FineWrapper extends EventEmitter {
|
||||||
|
|
||||||
if ( instances )
|
if ( instances )
|
||||||
for(const inst of instances) {
|
for(const inst of instances) {
|
||||||
if ( inst._reactInternalInstance && inst._reactInternalInstance._renderedComponent )
|
// How do we check mounted state for fibers?
|
||||||
inst._ffz_mounted = true;
|
//if ( inst._reactInternalInstance && inst._reactInternalInstance._renderedComponent )
|
||||||
|
// inst._ffz_mounted = true;
|
||||||
_instances.add(inst);
|
_instances.add(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ export class Tooltip {
|
||||||
if ( ! tip )
|
if ( ! tip )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tip.state = false;
|
tip.state = false;
|
||||||
|
|
||||||
if ( tip._show_timer ) {
|
if ( tip._show_timer ) {
|
||||||
clearTimeout(tip._show_timer);
|
clearTimeout(tip._show_timer);
|
||||||
|
|
|
@ -45,6 +45,11 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.(graphql|gql)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'graphql-tag/loader'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.(?:eot|ttf|woff|woff2)$/,
|
test: /\.(?:eot|ttf|woff|woff2)$/,
|
||||||
use: [{
|
use: [{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue