1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-09-17 02:16:54 +00:00

Add Browse -> Popular support for avatar and uptime (#359)

* Add Browse -> Popular support for avatar and uptime

* Fix potential null-erroring

* Fix Twitch's mess once again...

Also add actual decent support for the popular page and various other fixes, such as hiding blocked games and whatnot

* Fix...
This commit is contained in:
Lordmau5 2017-12-16 08:00:45 +01:00 committed by Mike
parent cc1705c0c8
commit bbf158aee1
3 changed files with 181 additions and 45 deletions

View file

@ -0,0 +1,104 @@
'use strict';
// ============================================================================
// Directory (Following, for now)
// ============================================================================
import {SiteModule} from 'utilities/module';
import {get} from 'utilities/object';
export default class BrowsePopular extends SiteModule {
constructor(...args) {
super(...args);
this.inject('site.apollo');
this.inject('site.fine');
this.inject('settings');
this.apollo.registerModifier('BrowsePage_Popular', `query {
streams {
edges {
node {
createdAt
broadcaster {
profileImageURL(width: 70)
}
}
}
}
}`);
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'
);
this.apollo.registerModifier('BrowsePage_Popular', res => this.modifyStreams(res), false);
}
onEnable() {
this.ChannelCard.ready((cls, instances) => {
// Popular Directory Channel Cards
this.apollo.ensureQuery(
'BrowsePage_Popular',
'data.streams.edges.node.0.createdAt'
);
for(const inst of instances) this.updateChannelCard(inst);
});
this.ChannelCard.on('update', this.updateChannelCard, this);
this.ChannelCard.on('mount', this.updateChannelCard, this);
this.ChannelCard.on('unmount', this.parent.clearUptime, this);
}
modifyStreams(res) { // eslint-disable-line class-methods-use-this
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
const newStreams = [];
const edges = get('data.streams.edges', res);
if (!edges) return res;
for (let i = 0; i < edges.length; i++) {
const edge = edges[i];
const node = edge.node;
const s = node.viewersCount = new Number(node.viewersCount || 0);
s.profileImageURL = node.broadcaster.profileImageURL;
s.createdAt = node.createdAt;
s.login = node.broadcaster.login;
s.displayName = node.broadcaster.displayName;
if (!node.game || node.game && !blockedGames.includes(node.game.name)) newStreams.push(edge);
}
res.data.streams.edges = newStreams;
return res;
}
updateChannelCard(inst) {
const container = this.fine.getHostNode(inst);
if (!container) return;
if (container.classList.contains('ffz-modified-channel-card')) return;
container.classList.add('ffz-modified-channel-card');
this.parent.updateUptime(inst, 'props.viewerCount.createdAt', '.tw-card-img');
this.parent.addCardAvatar(inst, 'props.viewerCount', '.tw-card');
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const hiddenPreview = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
if (inst.props.type === 'watch_party')
container.classList.toggle('tw-hide', this.settings.get('directory.hide-vodcasts'));
const img = container.querySelector && container.querySelector('.tw-card-img img');
if (img == null) return;
if (hiddenThumbnails.includes(inst.props.gameTitle)) {
img.src = hiddenPreview;
} else {
img.src = inst.props.imageSrc;
}
}
}

View file

@ -86,7 +86,8 @@ export default class Following extends SiteModule {
this.apollo.registerModifier('FollowingLive_CurrentUser', `query { this.apollo.registerModifier('FollowingLive_CurrentUser', `query {
currentUser { currentUser {
followedLiveUsers { followedLiveUsers {
nodes { edges {
node {
profileImageURL(width: 70) profileImageURL(width: 70)
stream { stream {
createdAt createdAt
@ -94,6 +95,7 @@ export default class Following extends SiteModule {
} }
} }
} }
}
}`); }`);
this.apollo.registerModifier('FollowingHosts_CurrentUser', `query { this.apollo.registerModifier('FollowingHosts_CurrentUser', `query {
@ -155,20 +157,28 @@ export default class Following extends SiteModule {
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || []; const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || []; const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
const newLiveNodes = []; const newStreams = [];
const nodes = res.data.currentUser.followedLiveUsers.nodes; const followedLiveUsers = get('data.currentUser.followedLiveUsers', res);
for (let i = 0; i < nodes.length; i++) { if (!followedLiveUsers)
const node = nodes[i]; return res;
const oldMode = !!followedLiveUsers.nodes;
const edgesOrNodes = followedLiveUsers.nodes || followedLiveUsers.edges;
for (let i = 0; i < edgesOrNodes.length; i++) {
const edge = edgesOrNodes[i];
const node = edge.node || edge;
const s = node.stream.viewersCount = new Number(node.stream.viewersCount || 0); const s = node.stream.viewersCount = new Number(node.stream.viewersCount || 0);
s.profileImageURL = node.profileImageURL; s.profileImageURL = node.profileImageURL;
s.createdAt = node.stream.createdAt; s.createdAt = node.stream.createdAt;
if (node.stream.game && hiddenThumbnails.includes(node.stream.game.name)) node.stream.previewImageURL = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg'; if (node.stream.game && hiddenThumbnails.includes(node.stream.game.name)) node.stream.previewImageURL = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
if (!node.stream.game || node.stream.game && !blockedGames.includes(node.stream.game.name)) newLiveNodes.push(node); if (!node.stream.game || node.stream.game && !blockedGames.includes(node.stream.game.name)) newStreams.push(edge);
} }
res.data.currentUser.followedLiveUsers.nodes = newLiveNodes; res.data.currentUser.followedLiveUsers[oldMode ? 'nodes' : 'edges'] = newStreams;
return res; return res;
} }
@ -176,12 +186,19 @@ export default class Following extends SiteModule {
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || []; const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || []; const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
const nodes = res.data.currentUser.followedHosts.nodes; const followedHosts = get('data.currentUser.followedHosts', res);
if (!followedHosts)
return res;
this.hosts = {}; this.hosts = {};
const newHostNodes = []; const newHostNodes = [];
for (let i = 0; i < nodes.length; i++) { const oldMode = !!followedHosts.nodes;
const node = nodes[i]; const edgesOrNodes = followedHosts.nodes || followedHosts.edges;
for (let i = 0; i < edgesOrNodes.length; i++) {
const edge = edgesOrNodes[i];
const node = edge.node || edge;
const s = node.hosting.stream.viewersCount = new Number(node.hosting.stream.viewersCount || 0); const s = node.hosting.stream.viewersCount = new Number(node.hosting.stream.viewersCount || 0);
s.profileImageURL = node.hosting.profileImageURL; s.profileImageURL = node.hosting.profileImageURL;
@ -194,7 +211,7 @@ export default class Following extends SiteModule {
channels: [node.displayName] channels: [node.displayName]
}; };
if (node.hosting.stream.game && hiddenThumbnails.includes(node.hosting.stream.game.name)) node.hosting.stream.previewImageURL = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg'; if (node.hosting.stream.game && hiddenThumbnails.includes(node.hosting.stream.game.name)) node.hosting.stream.previewImageURL = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
if (!node.hosting.stream.game || node.hosting.stream.game && !blockedGames.includes(node.hosting.stream.game.name)) newHostNodes.push(node); if (!node.hosting.stream.game || node.hosting.stream.game && !blockedGames.includes(node.hosting.stream.game.name)) newHostNodes.push(edge);
} else { } else {
this.hosts[node.hosting.displayName].nodes.push(node); this.hosts[node.hosting.displayName].nodes.push(node);
this.hosts[node.hosting.displayName].channels.push(node.displayName); this.hosts[node.hosting.displayName].channels.push(node.displayName);
@ -202,7 +219,7 @@ export default class Following extends SiteModule {
} }
if (this.settings.get('directory.following.group-hosts')) { if (this.settings.get('directory.following.group-hosts')) {
res.data.currentUser.followedHosts.nodes = newHostNodes; res.data.currentUser.followedHosts[oldMode ? 'nodes' : 'edges'] = newHostNodes;
} }
return res; return res;
} }
@ -220,12 +237,14 @@ export default class Following extends SiteModule {
n => n =>
get('data.currentUser.followedLiveUsers.nodes.0.profileImageURL', n) !== undefined get('data.currentUser.followedLiveUsers.nodes.0.profileImageURL', n) !== undefined
|| ||
get('data.currentUser.followedLiveUsers.edges.0.node.profileImageURL', n) !== undefined
||
get('data.currentUser.followedHosts.nodes.0.hosting.profileImageURL', n) !== undefined get('data.currentUser.followedHosts.nodes.0.hosting.profileImageURL', n) !== undefined
); );
} else if (this.router.match[1] === 'live') { } else if (this.router.match[1] === 'live') {
this.apollo.ensureQuery( this.apollo.ensureQuery(
'FollowingLive_CurrentUser', 'FollowingLive_CurrentUser',
'data.currentUser.followedLiveUsers.nodes.0.profileImageURL' 'data.currentUser.followedLiveUsers.nodes.0.profileImageURL' || 'data.currentUser.followedLiveUsers.edges.0.node.profileImageURL'
); );
} else if (this.router.match[1] === 'hosts') { } else if (this.router.match[1] === 'hosts') {
this.apollo.ensureQuery( this.apollo.ensureQuery(
@ -353,9 +372,10 @@ export default class Following extends SiteModule {
) )
); );
(document.body.querySelector('.twilight-root') || document.body).appendChild(this.hostMenu); const root = (document.body.querySelector('.twilight-root') || document.body);
root.appendChild(this.hostMenu);
this.hostMenuPopper = new Popper(document.body, this.hostMenu, { this.hostMenuPopper = new Popper(root, this.hostMenu, {
placement: 'bottom-start', placement: 'bottom-start',
modifiers: { modifiers: {
flip: { flip: {

View file

@ -12,6 +12,7 @@ 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 Community from './community';
import BrowsePopular from './browse_popular';
export default class Directory extends SiteModule { export default class Directory extends SiteModule {
constructor(...args) { constructor(...args) {
@ -30,6 +31,7 @@ export default class Directory extends SiteModule {
this.inject(Following); this.inject(Following);
this.inject(Game); this.inject(Game);
this.inject(Community); this.inject(Community);
this.inject(BrowsePopular);
this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false); this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false);
@ -134,6 +136,7 @@ export default class Directory extends SiteModule {
this.css_tweaks.toggleHide('boxart-hover', boxart === 1); this.css_tweaks.toggleHide('boxart-hover', boxart === 1);
this.ChannelCard.ready((cls, instances) => { this.ChannelCard.ready((cls, instances) => {
// Game Directory Channel Cards
this.apollo.ensureQuery( this.apollo.ensureQuery(
'GamePage_Game', 'GamePage_Game',
'data.directory.streams.edges.0.node.createdAt' 'data.directory.streams.edges.0.node.createdAt'
@ -149,37 +152,40 @@ export default class Directory extends SiteModule {
updateChannelCard(inst) { updateChannelCard(inst) {
const uptimeSel = inst.props.directoryType === 'GAMES' ? '.tw-thumbnail-card .tw-card-img' : '.tw-card .tw-aspect > div'; const container = this.fine.getHostNode(inst);
const avatarSel = inst.props.directoryType === 'GAMES' ? '.tw-thumbnail-card' : '.tw-card'; if (!container) return;
this.updateUptime(inst, 'props.streamNode.viewersCount.createdAt', uptimeSel); this.updateUptime(inst, 'props.streamNode.viewersCount.createdAt', '.tw-card-img');
this.addCardAvatar(inst, avatarSel); this.addCardAvatar(inst, 'props.streamNode.viewersCount', '.tw-card');
const type = inst.props.directoryType; const type = get('props.directoryType', inst);
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || []; const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const hiddenPreview = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg'; const hiddenPreview = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
const container = this.fine.getHostNode(inst); if (get('props.streamNode.type', inst) === 'watch_party' || get('props.type', inst) === 'watch_party')
if (inst.props.streamNode.type === 'watch_party')
container.classList.toggle('tw-hide', this.settings.get('directory.hide-vodcasts')); container.classList.toggle('tw-hide', this.settings.get('directory.hide-vodcasts'));
const img = container && container.querySelector && container.querySelector(`${uptimeSel} img`); const img = container.querySelector && container.querySelector('.tw-card-img img');
if (img === null) return; if (img == null) return;
if (type === 'GAMES' && hiddenThumbnails.includes(inst.props.directoryName) || if (type === 'GAMES' && hiddenThumbnails.includes(get('props.directoryName', inst)) ||
type === 'COMMUNITIES' && hiddenThumbnails.includes(inst.props.streamNode.game.name)) { type === 'COMMUNITIES' && hiddenThumbnails.includes(get('props.streamNode.game.name', inst))) {
img.src = hiddenPreview; img.src = hiddenPreview;
} else { } else {
img.src = inst.props.streamNode.previewImageURL; img.src = get('props.streamNode.previewImageURL', inst) || get('props.imageSrc', inst);
} }
} }
modifyStreams(res) { // eslint-disable-line class-methods-use-this modifyStreams(res) { // eslint-disable-line class-methods-use-this
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
const gamePage = get('data.directory.__typename', res) === 'Game';
const newStreams = []; const newStreams = [];
const edges = res.data.directory.streams.edges; const edges = get('data.directory.streams.edges', res);
if (!edges) return res;
for (let i = 0; i < edges.length; i++) { for (let i = 0; i < edges.length; i++) {
const edge = edges[i]; const edge = edges[i];
const node = edge.node; const node = edge.node;
@ -187,8 +193,10 @@ export default class Directory extends SiteModule {
const s = node.viewersCount = new Number(node.viewersCount || 0); const s = node.viewersCount = new Number(node.viewersCount || 0);
s.profileImageURL = node.broadcaster.profileImageURL; s.profileImageURL = node.broadcaster.profileImageURL;
s.createdAt = node.createdAt; s.createdAt = node.createdAt;
s.login = node.broadcaster.login;
s.displayName = node.broadcaster.displayName;
newStreams.push(edge); if (gamePage || (!node.game || node.game && !blockedGames.includes(node.game.name))) newStreams.push(edge);
} }
res.data.directory.streams.edges = newStreams; res.data.directory.streams.edges = newStreams;
return res; return res;
@ -256,10 +264,14 @@ export default class Directory extends SiteModule {
} }
addCardAvatar(inst, selector) { addCardAvatar(inst, created_path, selector) {
const container = this.fine.getHostNode(inst), const container = this.fine.getHostNode(inst),
card = container && container.querySelector && container.querySelector(selector), card = container && container.querySelector && container.querySelector(selector),
setting = this.settings.get('directory.show-channel-avatars'); setting = this.settings.get('directory.show-channel-avatars'),
data = get(created_path, inst);
if ( ! card )
return;
// Remove old elements // Remove old elements
const hiddenBodyCard = card.querySelector('.tw-card-body.tw-hide'); const hiddenBodyCard = card.querySelector('.tw-card-body.tw-hide');
@ -274,10 +286,10 @@ export default class Directory extends SiteModule {
if (channelAvatar !== null) if (channelAvatar !== null)
channelAvatar.remove(); channelAvatar.remove();
if ( ! card || setting === 0 ) if ( setting === 0 )
return; return;
if (inst.props.streamNode.viewersCount.profileImageURL) { if (data) {
if (setting === 1) { if (setting === 1) {
const cardDiv = card.querySelector('.tw-card-body'); const cardDiv = card.querySelector('.tw-card-body');
const modifiedDiv = e('div', { const modifiedDiv = e('div', {
@ -286,11 +298,11 @@ export default class Directory extends SiteModule {
const avatarDiv = e('a', { const avatarDiv = e('a', {
className: 'ffz-channel-avatar tw-mg-r-05 tw-mg-t-05', className: 'ffz-channel-avatar tw-mg-r-05 tw-mg-t-05',
href: `/${inst.props.streamNode.broadcaster.login}`, href: `/${data.login}`,
onclick: event => this.hijackUserClick(event, inst.props.streamNode.broadcaster.login) onclick: event => this.hijackUserClick(event, data.login)
}, e('img', { }, e('img', {
title: inst.props.streamNode.broadcaster.displayName, title: data.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL src: data.profileImageURL
})); }));
const cardDivParent = cardDiv.parentElement; const cardDivParent = cardDiv.parentElement;
@ -306,13 +318,13 @@ export default class Directory extends SiteModule {
} else if (setting === 2 || setting === 3) { } else if (setting === 2 || setting === 3) {
const avatarElement = e('a', { const avatarElement = e('a', {
className: 'ffz-channel-avatar', className: 'ffz-channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`, href: `/${data.login}`,
onclick: event => this.hijackUserClick(event, inst.props.streamNode.broadcaster.login) onclick: event => this.hijackUserClick(event, data.login)
}, e('div', 'live-channel-card__boxart tw-bottom-0 tw-absolute', }, e('div', 'live-channel-card__boxart tw-bottom-0 tw-absolute',
e('figure', 'tw-aspect tw-aspect--align-top', e('figure', 'tw-aspect tw-aspect--align-top',
e('img', { e('img', {
title: inst.props.streamNode.broadcaster.displayName, title: data.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL src: data.profileImageURL
}) })
) )
)); ));