1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-25 12:08:30 +00:00

Use ref-counting with socket topic subscriptions to make it easy to tell when we should and shouldn't actually unsubscribe. Add route awareness to FineWrapper so that we can unregister the MutationObserver when we know we won't be suddenly finding an instance that we're looking for. Have the channel bar register for the channel pubsub topic. Fix minimize navigation showing the navigation bar over top of theater mode.

This commit is contained in:
SirStendec 2018-03-14 13:58:04 -04:00
parent c559790f87
commit 254d297f79
17 changed files with 160 additions and 71 deletions

View file

@ -65,7 +65,7 @@ export default class Room {
if ( this.manager.rooms[this._login] === this )
this.manager.rooms[this._login] = null;
this.manager.socket.unsubscribe(`room.${this.login}`);
this.manager.socket.unsubscribe(this, `room.${this.login}`);
}
if ( this.manager.room_ids[this.id] === this )
@ -89,7 +89,7 @@ export default class Room {
const old_room = this.manager.rooms[this._login];
if ( old_room === this ) {
this.manager.rooms[this._login] = null;
this.manager.socket.unsubscribe(`room.${this.login}`);
this.manager.socket.unsubscribe(this, `room.${this.login}`);
}
}
@ -102,7 +102,7 @@ export default class Room {
old_room.login = null;
this.manager.rooms[val] = this;
this.manager.socket.subscribe(`room.${val}`);
this.manager.socket.subscribe(this, `room.${val}`);
this.manager.emit(':room-update-login', this, val);
}

View file

@ -50,8 +50,12 @@ export default class Twilight extends BaseSite {
this.router.on(':route', (route, match) => {
this.log.info('Navigation', route && route.name, match && match[0]);
this.fine.route(route && route.name);
});
const current = this.router.current;
this.fine.route(current && current.name);
document.head.appendChild(e('link', {
href: MAIN_URL,
rel: 'stylesheet',
@ -95,6 +99,19 @@ Twilight.KNOWN_MODULES = {
}
Twilight.CHAT_ROUTES = [
'collection',
'popout',
'video',
'user-videos',
'user-clips',
'user-events',
'user-followers',
'user-following',
'user'
]
Twilight.ROUTES = {
'front-page': '/',
'collection': '/collections/:collectionID',
@ -110,10 +127,13 @@ Twilight.ROUTES = {
'dir-all': '/directory/all/:filter?',
'dir-category': '/directory/:category?',
'event': '/event/:eventName',
'following': '/following',
'popout': '/popout',
'popout': '/popout/:userName/chat',
'video': '/videos/:videoID',
'user-videos': '/:userName/videos/:filter?',
'user-clips': '/:userName/manager/clips',
'user-clips': '/:userName/clips',
'user-collections': '/:userName/collections',
'user-events': '/:userName/events',
'user-followers': '/:userName/followers',
'user-following': '/:userName/following',
'user': '/:userName'
}

View file

@ -19,6 +19,7 @@ export default class ChannelBar extends Module {
this.inject('site.fine');
this.inject('site.apollo');
this.inject('metadata');
this.inject('socket');
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', CHANNEL_QUERY);
this.apollo.registerModifier('ChannelPage_ChannelInfoBar_User', data => {
@ -32,39 +33,60 @@ export default class ChannelBar extends Module {
this.ChannelBar = this.fine.define(
'channel-bar',
n => n.getTitle && n.getGame && n.renderGame
n => n.getTitle && n.getGame && n.renderGame,
['user']
);
this.HostBar = this.fine.define(
'host-container',
n => n.handleReportHosterClick
n => n.handleReportHosterClick,
['user']
)
}
onEnable() {
this.ChannelBar.on('unmount', this.unmountChannelBar, this);
this.ChannelBar.on('mount', this.updateChannelBar, this);
this.ChannelBar.on('update', this.updateChannelBar, this);
this.ChannelBar.ready((cls, instances) => {
for(const inst of instances)
this.updateChannelBar(inst);
});
this.ChannelBar.on('unmount', this.unmountChannelBar, this);
this.ChannelBar.on('mount', this.updateChannelBar, this);
this.ChannelBar.on('update', this.updateChannelBar, this);
/*this.HostBar.on('mount', inst => {
this.log.info('host-mount', inst, this.fine.getChildNode(inst));
});
/*this.HostBar.on('unmount', this.unmountHostBar, this);
this.HostBar.on('mount', this.updateHostBar, this);
this.HostBar.on('update', this.updateHostBar, this);
this.HostBar.ready((cls, instances) => {
for(const inst of instances)
this.log.info('host-found', inst, this.fine.getChildNode(inst));
})*/
this.updateHostBar(inst);
});*/
}
unmountChannelBar(inst) { // eslint-disable-line class-methods-use-this
updateChannelBar(inst) {
const login = inst.props.channelLogin;
if ( login !== inst._ffz_old_login ) {
if ( inst._ffz_old_login )
this.socket.unsubscribe(inst, `channel.${inst._ffz_old_login}`);
if ( login )
this.socket.subscribe(inst, `channel.${login}`);
inst._ffz_old_login = login;
}
this.updateMetadata(inst);
}
unmountChannelBar(inst) {
if ( inst._ffz_old_login ) {
this.socket.unsubscribe(inst, `channel.${inst._ffz_old_login}`);
inst._ffz_old_login = null;
}
const timers = inst._ffz_meta_timers;
if ( timers )
for(const key in timers)
@ -75,11 +97,6 @@ export default class ChannelBar extends Module {
}
updateChannelBar(inst) {
this.updateMetadata(inst);
}
updateMetadata(inst, keys) {
const container = this.fine.getChildNode(inst),
metabar = container && container.querySelector && container.querySelector('.channel-info-bar__action-container > .tw-flex');

View file

@ -10,6 +10,8 @@ import {has, split_chars} from 'utilities/object';
import Module from 'utilities/module';
import Twilight from 'site';
import Scroller from './scroller';
import ChatLine from './line';
import SettingsMenu from './settings_menu';
@ -114,17 +116,20 @@ export default class ChatHook extends Module {
this.ChatController = this.fine.define(
'chat-controller',
n => n.chatService
n => n.chatService,
Twilight.CHAT_ROUTES
);
this.ChatContainer = this.fine.define(
'chat-container',
n => n.showViewersList && n.onChatInputFocus
n => n.showViewersList && n.onChatInputFocus,
Twilight.CHAT_ROUTES
);
this.PinnedCheer = this.fine.define(
'pinned-cheer',
n => n.collapseCheer && n.saveRenderedMessageRef
n => n.collapseCheer && n.saveRenderedMessageRef,
Twilight.CHAT_ROUTES
);

View file

@ -4,6 +4,7 @@
// Chat Line
// ============================================================================
import Twilight from 'site';
import Module from 'utilities/module';
//import {Color} from 'utilities/color';
@ -25,12 +26,14 @@ export default class ChatLine extends Module {
this.ChatLine = this.fine.define(
'chat-line',
n => n.renderMessageBody && ! n.getMessageParts
n => n.renderMessageBody && ! n.getMessageParts,
Twilight.CHAT_ROUTES
);
this.ChatRoomLine = this.fine.define(
'chat-room-line',
n => n.renderMessageBody && n.getMessageParts
n => n.renderMessageBody && n.getMessageParts,
Twilight.CHAT_ROUTES
);
}
@ -46,7 +49,7 @@ export default class ChatLine extends Module {
const e = React.createElement;
this.ChatLine.ready((cls, instances) => {
this.ChatLine.ready(cls => {
cls.prototype.shouldComponentUpdate = function(props, state) {
const show = state.alwaysShowMessage || ! props.message.deleted,
old_show = this._ffz_show;

View file

@ -5,6 +5,7 @@
// ============================================================================
import {createElement as e} from 'utilities/dom';
import Twilight from 'site';
import Module from 'utilities/module';
export default class Scroller extends Module {
@ -19,7 +20,8 @@ export default class Scroller extends Module {
this.ChatScroller = this.fine.define(
'chat-scroller',
n => n.saveScrollRef && n.handleScrollEvent
n => n.saveScrollRef && n.handleScrollEvent,
Twilight.CHAT_ROUTES
);
this.settings.add('chat.scroller.freeze', {

View file

@ -4,7 +4,7 @@
// Chat Settings Menu
// ============================================================================
import {createElement as e} from 'utilities/dom';
import Twilight from 'site';
import Module from 'utilities/module';
export default class SettingsMenu extends Module {
@ -19,7 +19,8 @@ export default class SettingsMenu extends Module {
this.SettingsMenu = this.fine.define(
'chat-settings',
n => n.renderUniversalOptions && n.dismissRaidsTooltip
n => n.renderUniversalOptions && n.dismissRaidsTooltip,
Twilight.CHAT_ROUTES
);
}

View file

@ -1,7 +1,5 @@
.twilight-root {
.top-nav {
z-index: 9999;
> div {
height: 5rem !important;
}

View file

@ -21,7 +21,8 @@ export default class BrowsePopular extends SiteModule {
this.ChannelCard = this.fine.define(
'browse-all-channel-card',
n => n.props && n.props.channelName && n.props.linkTo && n.props.linkTo.state && n.props.linkTo.state.medium === 'twitch_browse_directory'
n => n.props && n.props.channelName && n.props.linkTo && n.props.linkTo.state && n.props.linkTo.state.medium === 'twitch_browse_directory',
['dir-all']
);
this.apollo.registerModifier('BrowsePage_Popular', res => this.modifyStreams(res), false);

View file

@ -70,7 +70,8 @@ export default class Following extends SiteModule {
this.ChannelCard = this.fine.define(
'following-channel-card',
n => n.renderGameBoxArt && n.renderContentType
n => n.renderGameBoxArt && n.renderContentType,
['dir-following']
);
this.apollo.registerModifier('FollowedIndex_CurrentUser', res => {

View file

@ -1,18 +1,5 @@
query {
directory {
... on Game {
streams {
edges {
node {
createdAt
type
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}
... on Community {
streams {
edges {
@ -26,5 +13,18 @@ query {
}
}
}
... on Game {
streams {
edges {
node {
createdAt
type
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}
}
}

View file

@ -21,7 +21,8 @@ export default class Game extends SiteModule {
this.GameHeader = this.fine.define(
'game-header',
n => n.renderFollowButton && n.renderGameDetailsTab
n => n.renderFollowButton && n.renderGameDetailsTab,
['dir-game-index', 'dir-community']
);
this.apollo.registerModifier('GamePage_Game', GAME_QUERY);

View file

@ -35,7 +35,8 @@ export default class Directory extends SiteModule {
this.ChannelCard = this.fine.define(
'channel-card',
n => n.props && n.props.streamNode
n => n.props && n.props.streamNode,
['dir-community', 'dir-game-index']
);

View file

@ -23,7 +23,8 @@ export default class Player extends Module {
this.Player = this.fine.define(
'twitch-player',
n => n.player && n.onPlayerReady
n => n.player && n.onPlayerReady,
['front-page', 'user', 'video']
);

View file

@ -32,7 +32,8 @@ export default class SubButton extends Module {
this.SubButton = this.fine.define(
'sub-button',
n => n.reportSubMenuAction && n.isUserDataReady
n => n.reportSubMenuAction && n.isUserDataReady,
['user', 'video']
);
}

View file

@ -43,7 +43,7 @@ export default class SocketClient extends Module {
this._want_connected = false;
this._topics = new Set;
this._topics = new Map;
this._pending = [];
this._awaiting = new Map;
@ -426,30 +426,42 @@ export default class SocketClient extends Module {
// Topics
// ========================================================================
subscribe(...topics) {
subscribe(referrer, ...topics) {
const t = this._topics;
for(const topic of topics) {
if ( this.connected && ! t.has(topic) )
if ( ! t.has(topic) ) {
if ( this.connected )
this._send('sub', topic);
t.add(topic);
t.set(topic, new Set);
}
const tp = t.get(topic);
tp.add(referrer);
}
}
unsubscribe(...topics) {
unsubscribe(referrer, ...topics) {
const t = this._topics;
for(const topic of topics) {
if ( this.connected && t.has(topic) )
this._send('unsub', topic);
if ( ! t.has(topic) )
continue;
const tp = t.get(topic);
tp.delete(referrer);
if ( ! tp.size ) {
t.delete(topic);
if ( this.connected )
this._send('unsub', topic);
}
}
}
get topics() {
return Array.from(this._topics);
return Array.from(this._topics.keys());
}
}

View file

@ -17,7 +17,8 @@ export default class Fine extends Module {
this._wrappers = new Map;
this._known_classes = new Map;
this._observer = null;
this._waiting = null;
this._waiting = [];
this._live_waiting = null;
}
@ -250,14 +251,39 @@ export default class Fine extends Module {
// Class Wrapping
// ========================================================================
define(key, criteria) {
route(route) {
this._route = route;
this._updateLiveWaiting();
}
_updateLiveWaiting() {
const lw = this._live_waiting = [],
crt = this._waiting_crit = [],
route = this._route;
if ( this._waiting )
for(const waiter of this._waiting)
if ( ! route || ! waiter.routes.length || waiter.routes.includes(route) ) {
lw.push(waiter);
crt.push(waiter.criteria);
}
if ( ! this._live_waiting.length )
this._stopWaiting();
else if ( ! this._waiting_timer )
this._startWaiting();
}
define(key, criteria, routes) {
if ( this._wrappers.has(key) )
return this._wrappers.get(key);
if ( ! criteria )
throw new Error('cannot find definition and no criteria provided');
const wrapper = new FineWrapper(key, criteria, this);
const wrapper = new FineWrapper(key, criteria, routes, this);
this._wrappers.set(key, wrapper);
const data = this.searchAll(this.react, [criteria], 1000)[0];
@ -266,11 +292,8 @@ export default class Fine extends Module {
this._known_classes.set(data.cls, wrapper);
} else {
if ( ! this._waiting )
this._startWaiting();
this._waiting.push(wrapper);
this._waiting_crit.push(criteria);
this._updateLiveWaiting();
}
return wrapper;
@ -278,7 +301,7 @@ export default class Fine extends Module {
_checkWaiters(nodes) {
if ( ! this._waiting )
if ( ! this._live_waiting )
return;
if ( ! Array.isArray(nodes) )
@ -287,12 +310,12 @@ export default class Fine extends Module {
for(let node of nodes) {
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 || ! this._waiting.length )
if ( ! node || ! this._live_waiting.length )
continue;
const data = this.searchAll(node, this._waiting_crit, 1000);
@ -300,26 +323,27 @@ export default class Fine extends Module {
while(i-- > 0) {
if ( data[i].cls ) {
const d = data[i],
w = this._waiting.splice(i, 1)[0];
w = this._live_waiting.splice(i, 1)[0];
this._waiting_crit.splice(i, 1);
this.log.info(`Found class for "${w.name}" at depth ${d.depth}`, d);
const idx = this._waiting.indexOf(w);
if ( idx !== -1 )
this._waiting.splice(idx, 1);
this.log.info(`Found class for "${w.name}" at depth ${d.depth}`, d);
w._set(d.cls, d.instances);
}
}
}
if ( ! this._waiting.length )
if ( ! this._live_waiting.length )
this._stopWaiting();
}
_startWaiting() {
this.log.info('Installing MutationObserver.');
this._waiting = [];
this._waiting_crit = [];
this._waiting_timer = setInterval(() => this._checkWaiters(), 500);
if ( ! this._observer )
@ -343,7 +367,7 @@ export default class Fine extends Module {
if ( this._waiting_timer )
clearInterval(this._waiting_timer);
this._waiting = null;
this._live_waiting = null;
this._waiting_crit = null;
this._waiting_timer = null;
}
@ -364,7 +388,7 @@ const EVENTS = {
export class FineWrapper extends EventEmitter {
constructor(name, criteria, fine) {
constructor(name, criteria, routes, fine) {
super();
this.name = name;
@ -372,6 +396,7 @@ export class FineWrapper extends EventEmitter {
this.fine = fine;
this.instances = new Set;
this.routes = routes || [];
this._wrapped = new Set;
this._class = null;