1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-03 00:18:31 +00:00

Address issues with Directory support (#342)

* Address issues with Directory support


De-duplicate `modifyStreams` for Game and Community

* More de-duplication!

PogChamp

* Early return for following.js

* Rely on `props.directoryType` instead of the router

* Move a method around to properly group them

* Fix for game buttons not properly showing when switching tabs

* More fixes!
This commit is contained in:
Lordmau5 2017-12-01 21:36:55 +01:00 committed by Mike
parent fcc3cab35f
commit 60f86033e8
5 changed files with 263 additions and 597 deletions

View file

@ -5,24 +5,12 @@
// ============================================================================
import {SiteModule} from 'utilities/module';
import {createElement as e} from 'utilities/dom';
import {duration_to_string} from 'utilities/time';
export default class Community extends SiteModule {
constructor(...args) {
super(...args);
this.inject('site.fine');
this.inject('site.router');
this.inject('site.apollo');
this.inject('site.css_tweaks');
this.inject('settings');
this.GameHeader = this.fine.define(
'game-header',
n => n.renderFollowButton && n.renderGameDetailsTab
);
this.apollo.registerModifier('GamePage_Game', `query {
directory {
@ -41,265 +29,5 @@ export default class Community extends SiteModule {
}
}
}`);
this.ChannelCard = this.fine.define(
'community-channel-card',
n => n.props && n.props.streamNode
);
this.apollo.registerModifier('GamePage_Game', res => this.router.current.name === 'dir-community' && this.modifyStreams(res), false);
this.on('settings:changed:show-channel-avatar', value => {
this.css_tweaks.toggleHide('profile-hover-game', value === 2);
this.router.current.name === 'dir-community' && this.ChannelCard.forceUpdate();
});
this.on('settings:changed:directory.following.uptime', () => this.router.current.name === 'dir-community' && this.ChannelCard.forceUpdate());
}
modifyStreams(res) { // eslint-disable-line class-methods-use-this
const newStreams = [];
const edges = res.data.directory.streams.edges;
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;
newStreams.push(edge);
}
res.data.directory.streams.edges = newStreams;
return res;
}
onEnable() {
this.GameHeader.ready((cls, instances) => {
if (this.router.current.name === 'dir-community') {
for(const inst of instances) this.updateButtons(inst);
}
});
this.ChannelCard.ready((cls, instances) => {
if (this.router.current.name === 'dir-community') {
this.apollo.ensureQuery(
'GamePage_Game',
'data.directory.streams.edges.0.node.createdAt'
);
for(const inst of instances) this.updateChannelCard(inst);
}
});
this.ChannelCard.on('update', inst => this.router.current.name === 'dir-community' && this.updateChannelCard(inst), this);
this.ChannelCard.on('mount', inst => this.router.current.name === 'dir-community' && this.updateChannelCard(inst), this);
this.ChannelCard.on('unmount', inst => this.router.current.name === 'dir-community' && this.updateUptime(inst), this);
this.css_tweaks.toggleHide('profile-hover-game', this.settings.get('directory.following.show-channel-avatar') === 2);
}
updateUptime(inst) {
const container = this.fine.getHostNode(inst);
const card = container && container.querySelector && container.querySelector('.tw-card');
if (container === null || card === null) {
if (inst.updateTimer !== undefined) {
clearInterval(inst.updateTimer);
inst.updateTimer = undefined;
return;
}
}
if (this.settings.get('directory.following.uptime') === 0) {
if (inst.updateTimer !== undefined) {
clearInterval(inst.updateTimer);
inst.updateTimer = undefined;
}
if (inst.uptimeElement !== undefined) {
inst.uptimeElement.remove();
inst.uptimeElementSpan = inst.uptimeElement = undefined;
}
} else {
if (inst.updateTimer === undefined) {
inst.updateTimer = setInterval(
this.updateUptime.bind(this, inst),
1000
);
}
const up_since = new Date(inst.props.streamNode.viewersCount.createdAt);
const uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
const uptimeText = duration_to_string(uptime, false, false, false, this.settings.get('directory.following.uptime') === 1);
if (uptime > 0) {
if (inst.uptimeElement === undefined) {
inst.uptimeElementSpan = e('span', 'tw-stat__value ffz-uptime', `${uptimeText}`);
inst.uptimeElement = e('div', {
className: 'c-background-overlay c-text-overlay font-size-6 top-0 right-0 z-default inline-flex absolute mg-05',
style: 'padding-left: 4px; padding-right: 4px;'
}, [
e('span', 'tw-stat__icon',
e('figure', 'ffz-i-clock')
),
inst.uptimeElementSpan
]);
if (card.querySelector('.ffz-uptime') === null) card.appendChild(inst.uptimeElement);
} else {
inst.uptimeElementSpan.textContent = `${uptimeText}`;
}
}
}
}
updateChannelCard(inst) {
this.updateUptime(inst);
const container = this.fine.getHostNode(inst);
const card = container && container.querySelector && container.querySelector('.tw-card');
if (container === null || card === null) return;
if (!inst.props.streamNode.viewersCount.createdAt) return;
// Remove old elements
const hiddenBodyCard = card.querySelector('.tw-card-body.hide');
if (hiddenBodyCard !== null) hiddenBodyCard.classList.remove('hide');
const ffzChannelData = card.querySelector('.ffz-channel-data');
if (ffzChannelData !== null) ffzChannelData.remove();
const channelAvatar = card.querySelector('.channel-avatar');
if (channelAvatar !== null) channelAvatar.remove();
if (inst.props.streamNode.viewersCount.profileImageURL) {
const avatarSetting = this.settings.get('directory.following.show-channel-avatar');
if (avatarSetting === 1) {
const cardDiv = card.querySelector('.tw-card-body');
const modifiedDiv = e('div', {
innerHTML: cardDiv.innerHTML
});
const avatarDiv = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
style: 'margin-right: 8px; min-width: 4rem; margin-top: 0.5rem;',
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.streamNode.broadcaster.login});
}
}, e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL,
style: 'height: 4rem;'
}));
const cardDivParent = cardDiv.parentElement;
if (cardDivParent.querySelector('.ffz-channel-data') === null) {
cardDiv.classList.add('hide');
const newCardDiv = e('div', 'ffz-channel-data flex flex-nowrap', [
avatarDiv, modifiedDiv
]);
cardDivParent.appendChild(newCardDiv);
}
} else if (avatarSetting === 2 || avatarSetting === 3) {
const avatarElement = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.streamNode.broadcaster.login});
}
}, e('div', 'live-channel-card__boxart bottom-0 absolute',
e('figure', 'tw-aspect tw-aspect--align-top',
e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL
})
)
)
);
const divToAppend = card.querySelector('figure.tw-aspect');
if (divToAppend.querySelector('.channel-avatar') === null) divToAppend.appendChild(avatarElement);
}
}
}
updateButtons(inst) {
const container = this.fine.getHostNode(inst);
// We can't get the buttons through querySelector('button ...') so this has to do for now...
const buttons = container && container.querySelector && container.querySelector('div > div.align-items-center');
const ffzButtons = buttons.querySelector('.ffz-buttons');
if (ffzButtons !== null) ffzButtons.remove();
if (buttons.querySelector('.ffz-buttons') === null) {
// Block / Unblock Games
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
const gameBlocked = blockedGames.includes(inst.props.directoryName);
const blockButton = e('button', {
className: 'mg-l-1 tw-button ffz-toggle-game-block',
style: `background-color: ${gameBlocked ? '#228B22' : '#B22222'};`
}, e('span', {
className: 'tw-button__text',
textContent: `${gameBlocked ? 'Unblock' : 'Block'}`
})
);
blockButton.addEventListener('click', () => {
const gameName = inst.props.directoryName;
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
if (blockedGames.includes(gameName)) blockedGames.splice(blockedGames.indexOf(gameName), 1);
else blockedGames.push(gameName);
this.settings.provider.set('directory.game.blocked-games', blockedGames);
this.updateButtons(inst);
});
// Hide / Unhide Thumbnails
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const thumbnailBlocked = hiddenThumbnails.includes(inst.props.directoryName);
const hideThumbnailButton = e('button', {
className: 'mg-l-1 tw-button ffz-toggle-thumbnail',
style: `background-color: ${thumbnailBlocked ? '#228B22' : '#B22222'};`
}, e('span', {
className: 'tw-button__text',
textContent: `${thumbnailBlocked ? 'Unhide Thumbnails' : 'Hide Thumbnails'}`
})
);
hideThumbnailButton.addEventListener('click', () => {
const gameName = inst.props.directoryName;
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
if (hiddenThumbnails.includes(gameName)) hiddenThumbnails.splice(hiddenThumbnails.indexOf(gameName), 1);
else hiddenThumbnails.push(gameName);
this.settings.provider.set('directory.game.hidden-thumbnails', hiddenThumbnails);
this.updateButtons(inst);
});
const ffzButtons = e('div', 'ffz-buttons', [
blockButton,
hideThumbnailButton
]);
buttons.appendChild(ffzButtons);
}
}
}

View file

@ -7,7 +7,6 @@
import {SiteModule} from 'utilities/module';
import {createElement as e} from 'utilities/dom';
import {get} from 'utilities/object';
import {duration_to_string} from 'utilities/time';
import Popper from 'popper.js';
@ -178,29 +177,25 @@ export default class Following extends SiteModule {
}`);
this.ChannelCard = this.fine.define(
'channel-card',
'following-channel-card',
n => n.renderGameBoxArt && n.renderContentType
);
this.apollo.registerModifier('FollowedIndex_CurrentUser', res => {
res = this.modifyLiveUsers(res);
res = this.modifyLiveHosts(res);
return res;
this.modifyLiveUsers(res);
this.modifyLiveHosts(res);
}, false);
this.apollo.registerModifier('FollowingLive_CurrentUser', res => this.modifyLiveUsers(res), false);
this.apollo.registerModifier('FollowingHosts_CurrentUser', res => this.modifyLiveHosts(res), false);
}
isRouteAcceptable() {
return this.router.current.name === 'dir-following-index'
return this.router.current.name === 'dir-following'
|| this.router.current.name === 'dir-category' && this.router.match[1] === 'following';
}
modifyLiveUsers(res) { // eslint-disable-line class-methods-use-this
if (!this.isRouteAcceptable()) return res;
modifyLiveUsers(res) {
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
@ -221,9 +216,7 @@ export default class Following extends SiteModule {
return res;
}
modifyLiveHosts(res) { // eslint-disable-line class-methods-use-this
if (!this.isRouteAcceptable()) return res;
modifyLiveHosts(res) {
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const blockedGames = this.settings.provider.get('directory.game.blocked-games') || [];
@ -266,8 +259,6 @@ export default class Following extends SiteModule {
this.css_tweaks.toggleHide('boxart-hide', this.settings.get('directory.following.hide-boxart') === 2);
this.css_tweaks.toggleHide('profile-hover-following', this.settings.get('directory.following.show-channel-avatar') === 2);
this.ChannelCard.on('update', inst => this.updateChannelCard(inst), this);
this.ChannelCard.ready((cls, instances) => {
if (this.router && this.router.match) {
if (this.router.match[1] === 'following') {
@ -294,6 +285,7 @@ export default class Following extends SiteModule {
for(const inst of instances) this.updateChannelCard(inst);
});
this.ChannelCard.on('update', inst => this.updateChannelCard(inst), this);
this.ChannelCard.on('mount', inst => this.updateChannelCard(inst), this);
this.ChannelCard.on('unmount', inst => this.parent.clearUptime(inst), this);
@ -301,7 +293,7 @@ export default class Following extends SiteModule {
}
destroyHostMenu(event) {
if (event.target.closest('.ffz-channel-selector-outer') === null && Date.now() > this.hostMenuBuffer) {
if (!event || event && event.target && event.target.closest('.ffz-channel-selector-outer') === null && Date.now() > this.hostMenuBuffer) {
this.hostMenuPopper && this.hostMenuPopper.destroy();
this.hostMenu && this.hostMenu.remove();
this.hostMenuPopper = this.hostMenu = undefined;
@ -332,14 +324,9 @@ export default class Following extends SiteModule {
simplebarContentChildren.push(
e('a', {
className: 'tw-interactable',
href: inst.props.linkTo.pathname,
href: `/${inst.props.viewerCount.hostData.channel}`,
style: 'padding-top: 0.1rem; padding-bottom: 0.1rem;',
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.linkTo.pathname.substring(1)});
}
onclick: event => this.parent.hijackUserClick(event, inst.props.viewerCount.hostData.channel, this.destroyHostMenu.bind(this))
}, e('div', 'align-items-center flex flex-row flex-nowrap mg-x-1 mg-y-05',
[
e('div', {
@ -374,6 +361,7 @@ export default class Following extends SiteModule {
className: 'tw-interactable',
href: `/${node.login}`,
style: 'padding-top: 0.1rem; padding-bottom: 0.1rem;',
onclick: event => this.parent.hijackUserClick(event, node.login, this.destroyHostMenu.bind(this))
}, e('div', 'align-items-center flex flex-row flex-nowrap mg-x-1 mg-y-05',
[
e('div', {
@ -427,6 +415,8 @@ export default class Following extends SiteModule {
}
updateChannelCard(inst) {
if (!this.isRouteAcceptable()) return;
this.parent.updateUptime(inst, 'props.viewerCount.createdAt', '.tw-card .tw-aspect > div');
const container = this.fine.getHostNode(inst),
@ -435,11 +425,6 @@ export default class Following extends SiteModule {
if ( container === null || card === null )
return;
const channelCardTitle = card.querySelector('.live-channel-card__title');
if ( channelCardTitle === null )
return;
// Remove old elements
const hiddenBodyCard = card.querySelector('.tw-card-body.hide');
if (hiddenBodyCard !== null) hiddenBodyCard.classList.remove('hide');
@ -451,7 +436,7 @@ export default class Following extends SiteModule {
if (channelAvatar !== null) channelAvatar.remove();
if (inst.props.viewerCount.profileImageURL) {
const hosting = inst.props.viewerCount.hostData;
const hosting = inst.props.channelNameLinkTo.state.content === 'live_host' && inst.props.viewerCount.hostData;
let channel, displayName;
if (hosting) {
channel = inst.props.viewerCount.hostData.channel;
@ -479,12 +464,7 @@ export default class Following extends SiteModule {
const avatarElement = e('a', {
className: 'channel-avatar',
href: hosting ? `/${channel}` : inst.props.linkTo.pathname,
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.streamNode.broadcaster.login});
}
onclick: event => this.parent.hijackUserClick(event, inst.props.streamNode.broadcaster.login)
}, e('div', 'live-channel-card__boxart bottom-0 absolute',
e('figure', 'tw-aspect tw-aspect--align-top',
e('img', {
@ -515,16 +495,17 @@ export default class Following extends SiteModule {
if (this.settings.get('directory.following.group-hosts')) {
const titleLink = card.querySelector('.ffz-channel-data a[data-a-target="live-channel-card-title-link"]');
const thumbnailLink = card.querySelector('a[data-a-target="live-channel-card-thumbnail-link"]');
const channelCardTitle = card.querySelector('.ffz-channel-data .live-channel-card__title');
if (hostObj.channels.length > 1) {
const textContent = `${hostObj.channels.length} hosting ${displayName}`;
const textContent = hostObj.channels.length > 1 ? `${hostObj.channels.length} hosting ${displayName}` : inst.props.title;
if (channelCardTitle !== null) {
channelCardTitle.textContent
= channelCardTitle.title
= textContent;
if (thumbnailLink !== null) thumbnailLink.title = textContent;
}
if (thumbnailLink !== null) thumbnailLink.title = textContent;
if (titleLink !== null) titleLink.onclick = this.showHostMenu.bind(this, inst, hostObj);
if (thumbnailLink !== null) thumbnailLink.onclick = this.showHostMenu.bind(this, inst, hostObj);
}

View file

@ -6,16 +6,13 @@
import {SiteModule} from 'utilities/module';
import {createElement as e} from 'utilities/dom';
import {duration_to_string} from 'utilities/time';
export default class Game extends SiteModule {
constructor(...args) {
super(...args);
this.inject('site.fine');
this.inject('site.router');
this.inject('site.apollo');
this.inject('site.css_tweaks');
this.inject('settings');
@ -41,223 +38,26 @@ export default class Game extends SiteModule {
}
}
}`);
this.ChannelCard = this.fine.define(
'game-channel-card',
n => n.props && n.props.streamNode
);
this.apollo.registerModifier('GamePage_Game', res => this.router.current.name === 'dir-game-index' && this.modifyStreams(res), false);
this.on('site.directory.following:update-show-channel-avatar', value => {
this.css_tweaks.toggleHide('profile-hover-game', value === 2);
this.router.current.name === 'dir-game-index' && this.ChannelCard.forceUpdate();
});
this.on('site.directory.following:update-uptime', () => this.router.current.name === 'dir-game-index' && this.ChannelCard.forceUpdate());
}
modifyStreams(res) { // eslint-disable-line class-methods-use-this
const newStreams = [];
// const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
// const thumbnailBlocked = hiddenThumbnails.includes(res.data.directory.displayName);
const edges = res.data.directory.streams.edges;
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;
// if (thumbnailBlocked) edge.node.previewImageURL = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
newStreams.push(edge);
}
res.data.directory.streams.edges = newStreams;
return res;
}
onEnable() {
this.GameHeader.ready((cls, instances) => {
if (this.router.current.name === 'dir-game-index') {
for(const inst of instances) this.updateButtons(inst);
}
for(const inst of instances) this.updateButtons(inst);
});
this.ChannelCard.ready((cls, instances) => {
if (this.router.current.name === 'dir-game-index') {
this.apollo.ensureQuery(
'GamePage_Game',
'data.directory.streams.edges.0.node.createdAt'
);
for(const inst of instances) this.updateChannelCard(inst);
}
});
this.ChannelCard.on('update', inst => this.router.current.name === 'dir-game-index' && this.updateChannelCard(inst), this);
this.ChannelCard.on('mount', inst => this.router.current.name === 'dir-game-index' && this.updateChannelCard(inst), this);
this.ChannelCard.on('unmount', inst => this.router.current.name === 'dir-game-index' && this.updateUptime(inst), this);
this.css_tweaks.toggleHide('profile-hover-game', this.settings.get('directory.following.show-channel-avatar') === 2);
this.GameHeader.on('update', inst => this.updateButtons(inst));
}
updateUptime(inst) {
const container = this.fine.getHostNode(inst);
const card = container && container.querySelector && container.querySelector('.tw-thumbnail-card');
updateButtons(inst, update = false) {
if (inst.props.directoryType !== 'GAMES') return;
if (container === null || card === null) {
if (inst.updateTimer !== undefined) {
clearInterval(inst.updateTimer);
inst.updateTimer = undefined;
return;
}
}
if (this.settings.get('directory.following.uptime') === 0) {
if (inst.updateTimer !== undefined) {
clearInterval(inst.updateTimer);
inst.updateTimer = undefined;
}
if (inst.uptimeElement !== undefined) {
inst.uptimeElement.remove();
inst.uptimeElementSpan = inst.uptimeElement = undefined;
}
} else {
if (inst.updateTimer === undefined) {
inst.updateTimer = setInterval(
this.updateUptime.bind(this, inst),
1000
);
}
const up_since = new Date(inst.props.streamNode.viewersCount.createdAt);
const uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
const uptimeText = duration_to_string(uptime, false, false, false, this.settings.get('directory.following.uptime') === 1);
if (uptime > 0) {
if (inst.uptimeElement === undefined) {
inst.uptimeElementSpan = e('span', 'tw-stat__value ffz-uptime', `${uptimeText}`);
inst.uptimeElement = e('div', {
className: 'c-background-overlay c-text-overlay font-size-6 top-0 right-0 z-default inline-flex absolute mg-05',
style: 'padding-left: 4px; padding-right: 4px;'
}, [
e('span', 'tw-stat__icon',
e('figure', 'ffz-i-clock')
),
inst.uptimeElementSpan
]);
if (card.querySelector('.ffz-uptime') === null) card.appendChild(inst.uptimeElement);
} else {
inst.uptimeElementSpan.textContent = `${uptimeText}`;
}
}
}
}
updateChannelCard(inst) {
this.updateUptime(inst);
const container = this.fine.getHostNode(inst);
const card = container && container.querySelector && container.querySelector('.tw-thumbnail-card');
if (container === null || card === null) return;
if (!inst.props.streamNode.viewersCount.createdAt || container === null || card === null) return;
// Remove old elements
const hiddenBodyCard = card.querySelector('.tw-card-body.hide');
if (hiddenBodyCard !== null) hiddenBodyCard.classList.remove('hide');
const ffzChannelData = card.querySelector('.ffz-channel-data');
if (ffzChannelData !== null) ffzChannelData.remove();
const channelAvatar = card.querySelector('.channel-avatar');
if (channelAvatar !== null) channelAvatar.remove();
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const thumbnailBlocked = hiddenThumbnails.includes(inst.props.directoryName);
if (thumbnailBlocked) {
card.classList.add('ffz-thumbnail-hidden');
} else {
card.classList.remove('ffz-thumbnail-hidden');
}
if (inst.props.streamNode.viewersCount.profileImageURL) {
const avatarSetting = this.settings.get('directory.following.show-channel-avatar');
if (avatarSetting === 1) {
const cardDiv = card.querySelector('.tw-card-body');
const modifiedDiv = e('div', {
innerHTML: cardDiv.innerHTML
});
const avatarDiv = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
style: 'margin-right: 8px; min-width: 4rem; margin-top: 0.5rem;',
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.streamNode.broadcaster.login});
}
}, e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL,
style: 'height: 4rem;'
}));
const cardDivParent = cardDiv.parentElement;
if (cardDivParent.querySelector('.ffz-channel-data') === null) {
cardDiv.classList.add('hide');
const newCardDiv = e('div', 'ffz-channel-data flex flex-nowrap', [
avatarDiv, modifiedDiv
]);
cardDivParent.appendChild(newCardDiv);
}
} else if (avatarSetting === 2 || avatarSetting === 3) {
const avatarElement = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
onclick: event => {
event.preventDefault();
event.stopPropagation();
this.router.navigate('user', { userName: inst.props.streamNode.broadcaster.login});
}
}, e('div', 'live-channel-card__boxart bottom-0 absolute',
e('figure', 'tw-aspect tw-aspect--align-top',
e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL
})
)
)
);
const divToAppend = card.querySelector('figure.tw-aspect');
if (divToAppend.querySelector('.channel-avatar') === null) divToAppend.appendChild(avatarElement);
}
}
}
updateButtons(inst) {
const container = this.fine.getHostNode(inst);
// We can't get the buttons through querySelector('button ...') so this has to do for now...
const buttons = container && container.querySelector && container.querySelector('div > div.align-items-center');
const ffzButtons = buttons.querySelector('.ffz-buttons');
if (ffzButtons !== null) ffzButtons.remove();
if (ffzButtons !== null && !update) return;
else if (ffzButtons) ffzButtons.remove();
if (buttons.querySelector('.ffz-buttons') === null) {
// Block / Unblock Games
@ -281,7 +81,7 @@ export default class Game extends SiteModule {
this.settings.provider.set('directory.game.blocked-games', blockedGames);
this.updateButtons(inst);
this.updateButtons(inst, true);
});
// Hide / Unhide Thumbnails
@ -305,8 +105,8 @@ export default class Game extends SiteModule {
this.settings.provider.set('directory.game.hidden-thumbnails', hiddenThumbnails);
this.updateButtons(inst);
this.ChannelCard.forceUpdate();
this.parent.ChannelCard.forceUpdate();
this.updateButtons(inst, true);
});
const ffzButtons = e('div', 'ffz-buttons', [

View file

@ -0,0 +1,234 @@
'use strict';
// ============================================================================
// Directory
// ============================================================================
import {SiteModule} from 'utilities/module';
import {duration_to_string} from 'utilities/time';
import {createElement as e} from 'utilities/dom';
import {get} from 'utilities/object';
import Following from './following';
import Game from './game';
import Community from './community';
export default class Directory extends SiteModule {
constructor(...args) {
super(...args);
this.should_enable = true;
this.inject('site.fine');
this.inject('site.router');
this.inject('site.apollo');
this.inject('site.css_tweaks');
this.inject('settings');
this.inject(Following);
this.inject(Game);
this.inject(Community);
this.apollo.registerModifier('GamePage_Game', res => this.modifyStreams(res), false);
this.ChannelCard = this.fine.define(
'channel-card',
n => n.props && n.props.streamNode
);
this.on('settings:changed:directory.following.show-channel-avatar', value => {
this.css_tweaks.toggleHide('profile-hover-game', value === 2);
this.ChannelCard.forceUpdate();
});
this.on('settings:changed:directory.following.uptime', () => this.ChannelCard.forceUpdate());
}
onEnable() {
this.css_tweaks.toggleHide('profile-hover-game', this.settings.get('directory.following.show-channel-avatar') === 2);
this.ChannelCard.ready((cls, instances) => {
this.apollo.ensureQuery(
'GamePage_Game',
'data.directory.streams.edges.0.node.createdAt'
);
for(const inst of instances) this.updateChannelCard(inst);
});
this.ChannelCard.on('update', inst => this.updateChannelCard(inst), this);
this.ChannelCard.on('mount', inst => this.updateChannelCard(inst), this);
this.ChannelCard.on('unmount', inst => this.clearUptime(inst), this);
}
updateChannelCard(inst) {
const uptimeSel = inst.props.directoryType === 'GAMES' ? '.tw-thumbnail-card .tw-card-img' : '.tw-card .tw-aspect > div';
const avatarSel = inst.props.directoryType === 'GAMES' ? '.tw-thumbnail-card' : '.tw-card';
this.updateUptime(inst, 'props.streamNode.viewersCount.createdAt', uptimeSel);
this.addCardAvatar(inst, avatarSel);
const type = inst.props.directoryType;
const hiddenThumbnails = this.settings.provider.get('directory.game.hidden-thumbnails') || [];
const hiddenPreview = 'https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg';
const container = this.fine.getHostNode(inst);
const img = container && container.querySelector && container.querySelector(`${uptimeSel} img`);
if (img === null) return;
if (type === 'GAMES' && hiddenThumbnails.includes(inst.props.directoryName) ||
type === 'COMMUNITIES' && hiddenThumbnails.includes(inst.props.streamNode.game.name)) {
img.src = hiddenPreview;
} else {
img.src = inst.props.streamNode.previewImageURL;
}
}
modifyStreams(res) { // eslint-disable-line class-methods-use-this
const newStreams = [];
const edges = res.data.directory.streams.edges;
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;
newStreams.push(edge);
}
res.data.directory.streams.edges = newStreams;
return res;
}
clearUptime(inst) { // eslint-disable-line class-methods-use-this
if ( inst.ffz_update_timer ) {
clearInterval(inst.ffz_update_timer);
inst.ffz_update_timer = null;
}
if ( inst.ffz_uptime_el ) {
inst.ffz_uptime_el.parentElement.removeChild(inst.ffz_uptime_el);
inst.ffz_uptime_el = null;
inst.ffz_uptime_span = null;
inst.ffz_uptime_tt = null;
}
}
updateUptime(inst, created_path, selector) {
const container = this.fine.getHostNode(inst),
card = container && container.querySelector && container.querySelector(selector),
setting = this.settings.get('directory.following.uptime'),
created_at = get(created_path, inst),
up_since = created_at && new Date(created_at),
uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
if ( ! card || setting === 0 || uptime < 1 )
return this.clearUptime(inst);
const up_text = duration_to_string(uptime, false, false, false, setting === 1);
if ( ! inst.ffz_uptime_el )
card.appendChild(inst.ffz_uptime_el = e('div',
'video-preview-card__preview-overlay-stat c-background-overlay c-text-overlay font-size-6 top-0 right-0 z-default inline-flex absolute mg-05',
e('div', 'tw-tooltip-wrapper inline-flex', [
e('div', 'tw-stat', [
e('span', 'c-text-live tw-stat__icon', e('figure', 'ffz-i-clock')),
inst.ffz_uptime_span = e('span', 'tw-stat__value')
]),
inst.ffz_uptime_tt = e('div', 'tw-tooltip tw-tooltip--down tw-tooltip--align-center')
])));
if ( ! inst.ffz_update_timer )
inst.ffz_update_timer = setInterval(this.updateUptime.bind(this, inst, created_path, selector), 1000);
inst.ffz_uptime_span.textContent = up_text;
inst.ffz_uptime_tt.textContent = up_since.toLocaleString();
}
addCardAvatar(inst, selector) {
const container = this.fine.getHostNode(inst),
card = container && container.querySelector && container.querySelector(selector),
setting = this.settings.get('directory.following.show-channel-avatar');
// Remove old elements
const hiddenBodyCard = card.querySelector('.tw-card-body.hide');
if (hiddenBodyCard !== null) hiddenBodyCard.classList.remove('hide');
const ffzChannelData = card.querySelector('.ffz-channel-data');
if (ffzChannelData !== null) ffzChannelData.remove();
const channelAvatar = card.querySelector('.channel-avatar');
if (channelAvatar !== null) channelAvatar.remove();
if ( ! card || setting === 0 )
return;
if (inst.props.streamNode.viewersCount.profileImageURL) {
if (setting === 1) {
const cardDiv = card.querySelector('.tw-card-body');
const modifiedDiv = e('div', {
innerHTML: cardDiv.innerHTML
});
const avatarDiv = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
style: 'margin-right: 8px; min-width: 4rem; margin-top: 0.5rem;',
onclick: event => this.hijackUserClick(event, inst.props.streamNode.broadcaster.login)
}, e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL,
style: 'height: 4rem;'
}));
const cardDivParent = cardDiv.parentElement;
if (cardDivParent.querySelector('.ffz-channel-data') === null) {
cardDiv.classList.add('hide');
const newCardDiv = e('div', 'ffz-channel-data flex flex-nowrap', [
avatarDiv, modifiedDiv
]);
cardDivParent.appendChild(newCardDiv);
}
} else if (setting === 2 || setting === 3) {
const avatarElement = e('a', {
className: 'channel-avatar',
href: `/${inst.props.streamNode.broadcaster.login}`,
onclick: event => this.hijackUserClick(event, inst.props.streamNode.broadcaster.login)
}, e('div', 'live-channel-card__boxart bottom-0 absolute',
e('figure', 'tw-aspect tw-aspect--align-top',
e('img', {
title: inst.props.streamNode.broadcaster.displayName,
src: inst.props.streamNode.viewersCount.profileImageURL
})
)
)
);
const divToAppend = card.querySelector('figure.tw-aspect');
if (divToAppend.querySelector('.channel-avatar') === null) divToAppend.appendChild(avatarElement);
}
}
}
hijackUserClick(event, user, optionalFn = null) {
event.preventDefault();
event.stopPropagation();
if (optionalFn) optionalFn();
this.router.navigate('user', { userName: user });
}
}

View file

@ -1,77 +0,0 @@
'use strict';
// ============================================================================
// Directory
// ============================================================================
import {SiteModule} from 'utilities/module';
import {duration_to_string} from 'utilities/time';
import {createElement as e} from 'utilities/dom';
import {get} from 'utilities/object';
import Following from './following';
import Game from './game';
import Community from './community';
export default class Directory extends SiteModule {
constructor(...args) {
super(...args);
this.should_enable = true;
this.inject('i18n');
this.inject('settings');
this.inject('site.fine');
this.inject(Following);
this.inject(Game);
this.inject(Community);
}
clearUptime(inst) { // eslint-disable-line class-methods-use-this
if ( inst.ffz_update_timer ) {
clearInterval(inst.ffz_update_timer);
inst.ffz_update_timer = null;
}
if ( inst.ffz_uptime_el ) {
inst.ffz_uptime_el.parentElement.removeChild(inst.ffz_uptime_el);
inst.ffz_uptime_el = null;
inst.ffz_uptime_span = null;
inst.ffz_uptime_tt = null;
}
}
updateUptime(inst, created_path, selector) {
const container = this.fine.getHostNode(inst),
card = container && container.querySelector && container.querySelector(selector),
setting = this.settings.get('directory.following.uptime'),
created_at = get(created_path, inst),
up_since = created_at && new Date(created_at),
uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
if ( ! card || setting === 0 || uptime < 1 )
return this.clearUptime(inst);
const up_text = duration_to_string(uptime, false, false, false, setting === 1);
if ( ! inst.ffz_uptime_el )
card.appendChild(inst.ffz_uptime_el = e('div',
'video-preview-card__preview-overlay-stat c-background-overlay c-text-overlay font-size-6 top-0 right-0 z-default inline-flex absolute mg-05',
e('div', 'tw-tooltip-wrapper inline-flex', [
e('div', 'tw-stat', [
e('span', 'c-text-live tw-stat__icon', e('figure', 'ffz-i-clock')),
inst.ffz_uptime_span = e('span', 'tw-stat__value')
]),
inst.ffz_uptime_tt = e('div', 'tw-tooltip tw-tooltip--down tw-tooltip--align-center')
])));
if ( ! inst.ffz_update_timer )
inst.ffz_update_timer = setInterval(this.updateUptime.bind(this, inst, created_path, selector), 1000);
inst.ffz_uptime_span.textContent = up_text;
inst.ffz_uptime_tt.textContent = up_since.toLocaleString();
}
}