1
0
Fork 0
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:
SirStendec 2018-02-22 18:23:44 -05:00
parent f0bcf7f53e
commit 0ae6e5021d
29 changed files with 536 additions and 263 deletions

View file

@ -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>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Setting to hide the Live indicator from live channels in the directory.</li>

View file

@ -18,11 +18,12 @@ export const CSS_BADGES = {
moderator: { 1: { color: '#34ae0a', svg: true } },
twitchbot: { 1: { color: '#34ae0a' } },
partner: { 1: { color: 'transparent', trans: { image: true, color: '#6441a5' } } },
'clip-champ': { 1: { color: '#6441a5'} },
turbo: { 1: { color: '#6441a5', svg: true } },
premium: { 1: { color: '#009cdc' } },
subscriber: { 0: { color: '#6441a4' }, 1: { color: '#6441a4' }},
subscriber: { 0: { color: '#6441a5' }, 1: { color: '#6441a5' }},
}
export const BADGE_POSITIONS = {
@ -143,13 +144,80 @@ export default class Badges extends Module {
this.style = new ManagedStyle('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', {
default: 0,
ui: {
path: 'Chat > Badges >> Appearance',
path: 'Chat > Badges >> tabs ~> Appearance',
title: 'Style',
component: 'setting-select-box',
data: [
@ -497,7 +565,7 @@ export default class Badges extends Module {
const b = {};
for(const data of badges) {
const sid = data.setID,
bs = b[sid] = b[sid] || {};
bs = b[sid] = b[sid] || {__game: /_\d+$/.test(sid)};
bs[data.version] = data;
}

View file

@ -429,7 +429,15 @@ export default class Chat extends Module {
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}];
if ( ! tokens[0].text )
return tokens;
for(const tokenizer of this.__tokenizers)
tokens = tokenizer.process.call(this, tokens, msg, user);

View 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>

View file

@ -39,7 +39,7 @@ export default class TooltipProvider extends Module {
onEnable() {
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,
delayHide: this.checkDelayHide.bind(this),
delayShow: this.checkDelayShow.bind(this),

View file

@ -38,10 +38,8 @@ export default class Twilight extends BaseSite {
}
onEnable() {
const root = this.fine.getParent(this.fine.react),
ctx = this.context = root && root._context,
store = this.store = ctx && ctx.store;
const thing = this.fine.searchTree(null, n => n.props && n.props.store),
store = this.store = thing && thing.props && thing.props.store;
if ( ! store )
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
@ -64,10 +62,10 @@ export default class Twilight extends BaseSite {
updateContext() {
try {
const state = this.store.getState(),
route = this.context.router && this.context.router.route;
history = this.router && this.router.history;
this.settings.updateContext({
location: route && route.location,
location: history && history.location,
ui: state && state.ui,
session: state && state.session
});

View file

@ -7,6 +7,9 @@
import Module from 'utilities/module';
import {deep_copy} from 'utilities/object';
import CHANNEL_QUERY from './channel_bar_query.gql';
export default class ChannelBar extends Module {
constructor(...args) {
super(...args);
@ -17,17 +20,9 @@ export default class ChannelBar extends Module {
this.inject('site.apollo');
this.inject('metadata');
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', this.apollo.gql`query {
user {
stream {
createdAt
type
}
}
}`);
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', CHANNEL_QUERY);
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', data => {
console.log('BOOP BOOP A DOOP', data);
const u = data && data.data && data.data.user;
if ( u ) {
const o = u.profileViewCount = new Number(u.profileViewCount || 0);

View file

@ -0,0 +1,8 @@
query {
user {
stream {
createdAt
type
}
}
}

View file

@ -61,25 +61,30 @@ const EVENTS = [
'onJoinedEvent',
'onDisconnectedEvent',
'onReconnectingEvent',
'onChatMessageEvent',
'onChatNoticeEvent',
'onChatActionEvent',
'onBadgesUpdatedEvent',
'onHostingEvent',
'onUnhostEvent',
'onChatMessageEvent',
'onChatActionEvent',
'onChatNoticeEvent',
'onTimeoutEvent',
'onBanEvent',
'onModerationEvent',
'onPurchaseEvent',
'onCrateEvent',
//'onRitualEvent',
'onSubscriptionEvent',
//'onResubscriptionEvent',
'onSubscriptionGiftEvent',
'onRoomStateEvent',
'onSlowModeEvent',
'onFollowerOnlyModeEvent',
'onSubscriberOnlyModeEvent',
'onTimeoutEvent',
'onBanEvent',
'onClearChatEvent',
'onRaidEvent',
'onUnraidEvent',
'onBadgesUpdatedEvent'
'onRoomModsEvent',
'onRoomStateEvent',
'onFollowerOnlyModeEvent',
'onSlowModeEvent',
'onSubscriberOnlyModeEvent',
'onEmoteOnlyModeEvent',
'onBitsCharityEvent'
];
@ -408,7 +413,7 @@ export default class ChatHook extends Module {
const i = this,
pm = this.postMessage;
for(const key of EVENTS) { // eslint-disable-line guard-for-in
for(const key of EVENTS) {
const original = this[key];
if ( original )
this[key] = function(e, t) {

View file

@ -25,7 +25,12 @@ export default class ChatLine extends Module {
this.ChatLine = this.fine.define(
'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 || {},
msg = this.props.message,
is_action = msg.type === types.Action,
user = msg.user,
is_action = msg.type === types.Action;
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),
/*bg_rgb = Color.RGBA.fromHex(user.color),
bg_color = bg_rgb.luminance() < .005 ? bg_rgb : bg_rgb.toHSLA().targetLuminance(0.005).toRGBA(),

View 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)
});
}
}

View file

@ -0,0 +1,12 @@
query {
streams {
edges {
node {
createdAt
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}

View file

@ -7,6 +7,8 @@
import {SiteModule} from 'utilities/module';
import {get} from 'utilities/object';
import BROWSE_POPULAR from './browse_popular.gql';
export default class BrowsePopular extends SiteModule {
constructor(...args) {
super(...args);
@ -15,18 +17,7 @@ export default class BrowsePopular extends SiteModule {
this.inject('site.fine');
this.inject('settings');
this.apollo.registerModifier('BrowsePage_Popular', this.apollo.gql`query {
streams {
edges {
node {
createdAt
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}`);
this.apollo.registerModifier('BrowsePage_Popular', BROWSE_POPULAR);
this.ChannelCard = this.fine.define(
'browse-all-channel-card',

View file

@ -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)
}
}
}
}
}
}
}`);
}
}

View file

@ -0,0 +1,13 @@
query {
currentUser {
followedLiveUsers {
nodes {
profileImageURL(width: 70)
stream {
type
createdAt
}
}
}
}
}

View file

@ -0,0 +1,15 @@
query {
currentUser {
followedHosts {
nodes {
profileImageURL(width: 70)
hosting {
profileImageURL(width: 70)
stream {
createdAt
}
}
}
}
}
}

View file

@ -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
}
}
}
}
}
}

View file

@ -0,0 +1,14 @@
query {
currentUser {
followedLiveUsers {
edges {
node {
profileImageURL(width: 70)
stream {
createdAt
}
}
}
}
}
}

View file

@ -10,6 +10,11 @@ import {get} from 'utilities/object';
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 {
constructor(...args) {
super(...args);
@ -58,75 +63,10 @@ export default class Following extends SiteModule {
changed: () => this.ChannelCard.forceUpdate()
});
this.apollo.registerModifier('FollowedIndex_CurrentUser', this.apollo.gql`query {
currentUser {
followedLiveUsers {
nodes {
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.apollo.registerModifier('FollowedIndex_CurrentUser', FOLLOWED_INDEX);
this.apollo.registerModifier('FollowingLive_CurrentUser', FOLLOWED_LIVE);
this.apollo.registerModifier('FollowingHosts_CurrentUser', FOLLOWED_HOSTS);
this.apollo.registerModifier('FollowedChannels', FOLLOWED_CHANNELS);
this.ChannelCard = this.fine.define(
'following-channel-card',

View 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)
}
}
}
}
}
}
}

View file

@ -7,6 +7,8 @@
import {SiteModule} from 'utilities/module';
import {createElement as e} from 'utilities/dom';
import GAME_QUERY from './game.gql';
export default class Game extends SiteModule {
constructor(...args) {
super(...args);
@ -22,23 +24,7 @@ export default class Game extends SiteModule {
n => n.renderFollowButton && n.renderGameDetailsTab
);
this.apollo.registerModifier('GamePage_Game', this.apollo.gql`query {
directory {
... on Game {
streams {
edges {
node {
createdAt
type
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}
}
}`);
this.apollo.registerModifier('GamePage_Game', GAME_QUERY);
}
onEnable() {

View file

@ -11,7 +11,6 @@ import {get} from 'utilities/object';
import Following from './following';
import Game from './game';
import Community from './community';
import BrowsePopular from './browse_popular';
export default class Directory extends SiteModule {
@ -30,7 +29,6 @@ export default class Directory extends SiteModule {
this.inject(Following);
this.inject(Game);
this.inject(Community);
this.inject(BrowsePopular);
this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false);

View file

@ -192,12 +192,12 @@ export default class Player extends Module {
disableAutoplay(inst) {
if ( ! inst.player ) {
this.log.warn("disableAutoplay() called but Player was not ready");
this.log.warn('disableAutoplay() called but Player was not ready');
return;
}
if ( ! inst.ffzAutoplay ) {
var playListener = () => {
const playListener = () => {
this.log.info('Auto-paused player');
inst.ffzAutoplay = null;
inst.player.pause();
@ -209,6 +209,7 @@ export default class Player extends Module {
inst.player.removeEventListener('contentShowing', playListener);
}, 1000);
}
inst.ffzAutoplay = playListener;
inst.player.addEventListener('play', inst.ffzAutoplay);
inst.player.addEventListener('playing', inst.ffzAutoplay);

View file

@ -7,7 +7,6 @@
import Module from 'utilities/module';
import {has, get} from 'utilities/object';
import gql from 'graphql-tag';
export default class Apollo extends Module {
constructor(...args) {
@ -16,17 +15,8 @@ export default class Apollo extends Module {
this.modifiers = {};
this.post_modifiers = {};
this.gql = gql;
this.inject('..web_munch');
this.inject('..fine');
this.registerModifier('ViewerCard', gql`query {
targetUser: user {
createdAt
profileViewCount
}
}`);
}
async onEnable() {
@ -34,10 +24,10 @@ export default class Apollo extends Module {
let client = this.client;
if ( ! client ) {
const root = this.fine.getParent(this.fine.react),
ctx = root && root._context;
const root = this.fine.react,
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');

View file

@ -22,10 +22,8 @@ export default class FineRouter extends Module {
}
onEnable() {
const root = this.fine.getParent(this.fine.react),
ctx = this.context = root && root._context,
router = ctx && ctx.router,
history = this.history = router && router.history;
const thing = this.fine.searchTree(null, n => n.props && n.props.history),
history = this.history = thing && thing.props && thing.props.history;
if ( ! history )
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());

View file

@ -24,20 +24,21 @@ export default class Fine extends Module {
async onEnable(tries=0) {
// TODO: Move awaitElement to utilities/dom
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 ( ! accessor ) {
if ( ! this.root_element || ! this.root_element._reactRootContainer ) {
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));
}
this.react = this.getReactInstance(this.root_element);
this.react_root = this.root_element._reactRootContainer;
this.react = this.react_root.current.child;
}
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) {
if ( ! this.accessor )
this.accessor = Fine.findAccessor(element);
if ( ! this.accessor )
return;
return element[this.accessor];
}
getOwner(instance) {
if ( instance._reactInternalInstance )
instance = instance._reactInternalInstance;
if ( instance._reactInternalFiber )
instance = instance._reactInternalFiber;
else if ( instance instanceof Node )
instance = this.getReactInstance(instance);
if ( ! instance )
return null;
return instance._owner || (instance._currentElement && instance._currentElement._owner);
return instance.return;
}
getHostNode(instance) { //eslint-disable-line class-methods-use-this
if ( instance._reactInternalInstance )
instance = instance._reactInternalInstance;
getHostNode(instance) {
if ( instance._reactInternalFiber )
instance = instance._reactInternalFiber;
else if ( instance instanceof Node )
instance = this.getReactInstance(instance);
while( instance )
if ( instance._hostNode )
return instance._hostNode;
if ( instance.stateNode instanceof Node )
return instance.stateNode
else
instance = instance._renderedComponent;
instance = instance.parent;
}
getParent(instance) {
const owner = this.getOwner(instance);
return owner && this.getOwner(owner);
return this.getOwner(instance);
}
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) {
if ( node._reactInternalInstance )
node = node._reactInternalInstance;
if ( node._reactInternalFiber )
node = node._reactInternalFiber;
else if ( node instanceof Node )
node = this.getReactInstance(node);
if ( ! node || depth > max_depth )
return null;
const inst = node._instance;
const inst = node.stateNode;
if ( inst && criteria(inst) )
return inst;
if ( node._currentElement && node._currentElement._owner ) {
const result = this.searchParent(node._currentElement._owner, criteria, max_depth, depth+1);
if ( node.return ) {
const result = this.searchParent(node.return, criteria, max_depth, depth+1);
if ( result )
return result;
}
if ( node._hostParent )
return this.searchParent(node._hostParent, criteria, max_depth, depth+1);
return null;
}
searchTree(node, criteria, max_depth=15, depth=0) {
if ( ! node )
node = this.react;
else if ( node._reactInternalInstance )
node = node._reactInternalInstance;
else if ( node._reactInternalFiber )
node = node._reactInternalFiber;
else if ( node instanceof Node )
node = this.getReactInstance(node);
if ( ! node || depth > max_depth )
return null;
const inst = node._instance;
const inst = node.stateNode;
if ( inst && criteria(inst) )
return inst;
const children = node._renderedChildren,
component = node._renderedComponent;
if ( children )
for(const key in children)
if ( has(children, key) ) {
const child = children[key];
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);
if ( node.child ) {
let child = node.child;
while(child) {
const result = this.searchTree(child, criteria, max_depth, depth+1);
if ( result )
return result;
child = child.sibling;
}
}
}
searchAll(node, criterias, max_depth=15, depth=0, data) {
if ( ! node )
node = this.react;
else if ( node._reactInternalInstance )
node = node._reactInternalInstance;
else if ( node._reactInternalFiber )
node = node._reactInternalFiber;
else if ( node instanceof Node )
node = this.getReactInstance(node);
@ -167,7 +195,7 @@ export default class Fine extends Module {
if ( depth > data.max_depth )
data.max_depth = depth;
const inst = node._instance;
const inst = node.stateNode;
if ( inst ) {
const cls = inst.constructor,
idx = data.classes.indexOf(cls);
@ -189,18 +217,13 @@ export default class Fine extends Module {
}
}
const children = node._renderedChildren,
component = node._renderedComponent;
if ( children )
for(const key in children)
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);
if ( node.child ) {
let child = node.child;
while(child) {
this.searchAll(child, criterias, max_depth, depth+1, data);
child = child.sibling;
}
}
return data.out;
}
@ -400,8 +423,9 @@ export class FineWrapper extends EventEmitter {
if ( instances )
for(const inst of instances) {
if ( inst._reactInternalInstance && inst._reactInternalInstance._renderedComponent )
inst._ffz_mounted = true;
// How do we check mounted state for fibers?
//if ( inst._reactInternalInstance && inst._reactInternalInstance._renderedComponent )
// inst._ffz_mounted = true;
_instances.add(inst);
}

View file

@ -160,7 +160,7 @@ export class Tooltip {
if ( ! tip )
return;
tip.state = false;
tip.state = false;
if ( tip._show_timer ) {
clearTimeout(tip._show_timer);

View file

@ -45,6 +45,11 @@ module.exports = {
}
}]
},
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader'
},
{
test: /\.(?:eot|ttf|woff|woff2)$/,
use: [{