mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-05 18:48:31 +00:00
4.0.0-rc13.4
* Fixed: Game directory pages not loading. * Changed: Rewrite GraphQL query merging logic to hopefully perform in a more robust manner less likely to break in the future.
This commit is contained in:
parent
7fc27ff025
commit
551a08bfd0
12 changed files with 158 additions and 186 deletions
|
@ -100,7 +100,7 @@ class FrankerFaceZ extends Module {
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-rc13.3',
|
major: 4, minor: 0, revision: 0, extra: '-rc13.4',
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
query {
|
fragment browsePagePopularStreamsWithTagsEdge2 on StreamEdge {
|
||||||
streams {
|
node {
|
||||||
edges {
|
createdAt
|
||||||
node {
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,18 @@
|
||||||
query {
|
query FollowedChannels_RENAME2 {
|
||||||
currentUser {
|
currentUser {
|
||||||
|
follows {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
stream {
|
||||||
|
createdAt
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
followedLiveUsers {
|
followedLiveUsers {
|
||||||
nodes {
|
nodes {
|
||||||
stream {
|
stream {
|
||||||
type
|
|
||||||
createdAt
|
createdAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
query {
|
query FollowingHosts_CurrentUser {
|
||||||
currentUser {
|
currentUser {
|
||||||
followedHosts {
|
followedHosts {
|
||||||
nodes {
|
nodes {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
query {
|
query FollowedIndex_CurrentUser {
|
||||||
currentUser {
|
currentUser {
|
||||||
followedLiveUsers {
|
followedLiveUsers {
|
||||||
nodes {
|
nodes {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
query {
|
query FollowingLive_CurrentUser {
|
||||||
currentUser {
|
currentUser {
|
||||||
followedLiveUsers {
|
followedLiveUsers {
|
||||||
edges {
|
edges {
|
||||||
|
|
|
@ -1,24 +1,9 @@
|
||||||
query {
|
query {
|
||||||
directory {
|
__type
|
||||||
... on Community {
|
}
|
||||||
streams {
|
|
||||||
edges {
|
fragment directoryPageGameStreamWithTagsEdge on StreamEdge {
|
||||||
node {
|
node {
|
||||||
createdAt
|
createdAt
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on Game {
|
|
||||||
streams {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
createdAt
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,71 +21,6 @@ export default class Game extends SiteModule {
|
||||||
this.inject('i18n');
|
this.inject('i18n');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
|
||||||
/*this.metadata.definitions.block_game = {
|
|
||||||
type: 'directory',
|
|
||||||
button(data) {
|
|
||||||
return `ffz-directory-toggle-block${data.blocked ? ' active' : ''}`
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(data) {
|
|
||||||
if ( data.type !== 'GAMES' )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const blocked_games = this.settings.provider.get('directory.game.blocked-games', []),
|
|
||||||
blocked = blocked_games.includes(data.name);
|
|
||||||
|
|
||||||
data.blocked = blocked;
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
label(data) {
|
|
||||||
if ( ! data )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return data.blocked ?
|
|
||||||
this.i18n.t('directory.unblock', 'Unblock') :
|
|
||||||
this.i18n.t('directory.block', 'Block')
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip() {
|
|
||||||
return this.i18n.t('directory.block-explain', 'This will let you block streams playing this game from showing up in the directory.');
|
|
||||||
},
|
|
||||||
|
|
||||||
click: this.generateClickHandler('directory.game.blocked-games')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.metadata.definitions.hide_thumbnails = {
|
|
||||||
type: 'directory',
|
|
||||||
button(data) {
|
|
||||||
return `ffz-directory-toggle-thumbnail${data.hidden ? ' active' : ''}`
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(data) {
|
|
||||||
if ( data.type !== 'GAMES' )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const hidden_games = this.settings.provider.get('directory.game.hidden-thumbnails', []);
|
|
||||||
|
|
||||||
data.hidden = hidden_games.includes(data.name);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
label(data) {
|
|
||||||
if ( ! data )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return data.hidden ?
|
|
||||||
this.i18n.t('directory.show-thumbnails', 'Show Thumbnails') :
|
|
||||||
this.i18n.t('directory.hide-thumbnails', 'Hide Thumbnails');
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip() {
|
|
||||||
return this.i18n.t('directory.thumbnails-explain', 'Enabling this will hide thumbnails of this game everywhere in the directory.');
|
|
||||||
},
|
|
||||||
|
|
||||||
click: this.generateClickHandler('directory.game.hidden-thumbnails')
|
|
||||||
}*/
|
|
||||||
|
|
||||||
this.GameHeader = this.fine.define(
|
this.GameHeader = this.fine.define(
|
||||||
'game-header',
|
'game-header',
|
||||||
n => n.props && n.props.data && n.renderDropsAvailable,
|
n => n.props && n.props.data && n.renderDropsAvailable,
|
||||||
|
@ -93,6 +28,16 @@ export default class Game extends SiteModule {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.apollo.registerModifier('DirectoryPage_Game', GAME_QUERY);
|
this.apollo.registerModifier('DirectoryPage_Game', GAME_QUERY);
|
||||||
|
this.apollo.registerModifier('DirectoryPage_Game', res => this.modifyStreams(res), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyStreams(res) { // eslint-disable-line class-methods-use-this
|
||||||
|
const edges = get('data.game.streams.edges', res);
|
||||||
|
if ( ! edges || ! edges.length )
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res.data.game.streams.edges = this.parent.processNodes(edges);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
|
|
@ -46,8 +46,6 @@ export default class Directory extends SiteModule {
|
||||||
this.inject(Game);
|
this.inject(Game);
|
||||||
this.inject(BrowsePopular);
|
this.inject(BrowsePopular);
|
||||||
|
|
||||||
this.apollo.registerModifier('DirectoryPage_Game', res => this.modifyStreams(res), false);
|
|
||||||
|
|
||||||
this.DirectoryCard = this.fine.define(
|
this.DirectoryCard = this.fine.define(
|
||||||
'directory-card',
|
'directory-card',
|
||||||
n => n.renderTitles && n.renderIconicImage,
|
n => n.renderTitles && n.renderIconicImage,
|
||||||
|
@ -217,7 +215,7 @@ export default class Directory extends SiteModule {
|
||||||
// TODO: Better query handling.
|
// TODO: Better query handling.
|
||||||
this.apollo.ensureQuery(
|
this.apollo.ensureQuery(
|
||||||
'DirectoryPage_Game',
|
'DirectoryPage_Game',
|
||||||
'data.directory.streams.edges.0.node.createdAt'
|
'data.game.streams.edges.0.node.createdAt'
|
||||||
);
|
);
|
||||||
|
|
||||||
//for(const inst of instances)
|
//for(const inst of instances)
|
||||||
|
@ -294,18 +292,6 @@ export default class Directory extends SiteModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
modifyStreams(res) { // eslint-disable-line class-methods-use-this
|
|
||||||
const is_game_query = get('data.directory.__typename', res) === 'Game',
|
|
||||||
edges = get('data.directory.streams.edges', res);
|
|
||||||
|
|
||||||
if ( ! edges || ! edges.length )
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res.data.directory.streams.edges = this.processNodes(edges, is_game_query);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
clearUptime(inst) { // eslint-disable-line class-methods-use-this
|
clearUptime(inst) { // eslint-disable-line class-methods-use-this
|
||||||
if ( inst.ffz_update_timer ) {
|
if ( inst.ffz_update_timer ) {
|
||||||
clearInterval(inst.ffz_update_timer);
|
clearInterval(inst.ffz_update_timer);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
query {
|
query RecommendedChannels {
|
||||||
currentUser {
|
currentUser {
|
||||||
recommendations {
|
recommendations {
|
||||||
liveRecommendations {
|
liveRecommendations {
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {has, get} from 'utilities/object';
|
import {get, deep_copy} from 'utilities/object';
|
||||||
|
import merge from 'utilities/graphql';
|
||||||
|
|
||||||
|
|
||||||
const BAD_ERRORS = [
|
/*const BAD_ERRORS = [
|
||||||
'timeout',
|
'timeout',
|
||||||
'unable to load',
|
'unable to load',
|
||||||
'error internal',
|
'error internal',
|
||||||
|
@ -31,7 +32,7 @@ function skip_error(err) {
|
||||||
for(const m of BAD_ERRORS)
|
for(const m of BAD_ERRORS)
|
||||||
if ( err.message.includes(m) )
|
if ( err.message.includes(m) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
export class GQLError extends Error {
|
export class GQLError extends Error {
|
||||||
|
@ -212,6 +213,9 @@ export default class Apollo extends Module {
|
||||||
query = query_map && query_map.get(id),
|
query = query_map && query_map.get(id),
|
||||||
modifiers = this.modifiers[operation];
|
modifiers = this.modifiers[operation];
|
||||||
|
|
||||||
|
|
||||||
|
const pre_modification = deep_copy(request.query);
|
||||||
|
|
||||||
if ( modifiers ) {
|
if ( modifiers ) {
|
||||||
for(const mod of modifiers) {
|
for(const mod of modifiers) {
|
||||||
if ( typeof mod === 'function' )
|
if ( typeof mod === 'function' )
|
||||||
|
@ -245,6 +249,8 @@ export default class Apollo extends Module {
|
||||||
this.log.info('Unable to find GQL Print. Clearing store for query:', operation);
|
this.log.info('Unable to find GQL Print. Clearing store for query:', operation);
|
||||||
this.client.queryManager.queryStore.store[id] = null;
|
this.client.queryManager.queryStore.store[id] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log.info('Query', operation, pre_modification, request.query, request.variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
apolloPostFlight(response) {
|
apolloPostFlight(response) {
|
||||||
|
@ -406,71 +412,3 @@ export default class Apollo extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Query Merging
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
function canMerge(a, b) {
|
|
||||||
return a.kind === b.kind &&
|
|
||||||
a.kind !== 'FragmentDefinition' &&
|
|
||||||
(a.selectionSet == null) === (b.selectionSet == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function merge(a, b) {
|
|
||||||
if ( ! canMerge(a, b) )
|
|
||||||
return a;
|
|
||||||
|
|
||||||
if ( a.definitions ) {
|
|
||||||
const a_def = a.definitions,
|
|
||||||
b_def = b.definitions;
|
|
||||||
|
|
||||||
for(let i=0; i < a_def.length && i < b_def.length; i++)
|
|
||||||
a_def[i] = merge(a_def[i], b_def[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( a.selectionSet ) {
|
|
||||||
const s = a.selectionSet.selections,
|
|
||||||
selects = {};
|
|
||||||
for(const sel of b.selectionSet.selections) {
|
|
||||||
const name = sel.kind === 'InlineFragment' ?
|
|
||||||
(sel.typeCondition.name ?
|
|
||||||
sel.typeCondition.name.value : null) :
|
|
||||||
(sel.name ? sel.name.value : null),
|
|
||||||
alias = sel.alias ? sel.alias.value : null,
|
|
||||||
key = `${name}:${alias}`;
|
|
||||||
|
|
||||||
if ( name )
|
|
||||||
selects[key] = sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let i=0, l = s.length; i < l; i++) {
|
|
||||||
const sel = s[i],
|
|
||||||
name = sel.kind === 'InlineFragment' ?
|
|
||||||
(sel.typeCondition.name ?
|
|
||||||
sel.typeCondition.name.value : null) :
|
|
||||||
(sel.name ? sel.name.value : null),
|
|
||||||
alias = sel.alias ? sel.alias.value : null,
|
|
||||||
key = `${name}:${alias}`,
|
|
||||||
other = selects[key];
|
|
||||||
|
|
||||||
if ( other ) {
|
|
||||||
s[i] = merge(sel, other);
|
|
||||||
selects[key] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const key in selects)
|
|
||||||
if ( has(selects, key) ) {
|
|
||||||
const val = selects[key];
|
|
||||||
if ( val )
|
|
||||||
s.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Variables?
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
113
src/utilities/graphql.js
Normal file
113
src/utilities/graphql.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import { deep_copy } from "./object";
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GraphQL Document Manipulation
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export const MERGE_METHODS = {
|
||||||
|
Document: (a, b) => {
|
||||||
|
if ( a.definitions && b.definitions )
|
||||||
|
a.definitions = mergeList(a.definitions, b.definitions);
|
||||||
|
else if ( b.definitions )
|
||||||
|
a.definitions = b.definitions;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
|
||||||
|
Field: (a, b) => {
|
||||||
|
if ( a.name && (! b.name || b.name.value !== a.name.value) )
|
||||||
|
return a;
|
||||||
|
|
||||||
|
// TODO: arguments
|
||||||
|
// TODO: directives
|
||||||
|
|
||||||
|
if ( a.selectionSet && b.selectionSet )
|
||||||
|
a.selectionSet = merge(a.selectionSet, b.selectionSet);
|
||||||
|
else if ( b.selectionSet )
|
||||||
|
a.selectionSet = b.selectionSet;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
|
||||||
|
OperationDefinition: (a, b) => {
|
||||||
|
if ( a.operation !== b.operation )
|
||||||
|
return a;
|
||||||
|
|
||||||
|
// TODO: variableDefinitions
|
||||||
|
// TODO: directives
|
||||||
|
|
||||||
|
if ( a.selectionSet && b.selectionSet )
|
||||||
|
a.selectionSet = merge(a.selectionSet, b.selectionSet);
|
||||||
|
else if ( b.selectionSet )
|
||||||
|
a.selectionSet = b.selectionSet;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
|
||||||
|
FragmentDefinition: (a, b) => {
|
||||||
|
if ( a.typeCondition && b.typeCondition ) {
|
||||||
|
if ( a.typeCondition.kind !== b.typeCondition.kind )
|
||||||
|
return a;
|
||||||
|
|
||||||
|
if ( a.typeCondition.name.value != b.typeCondition.name.value )
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: directives
|
||||||
|
|
||||||
|
if ( a.selectionSet && b.selectionSet )
|
||||||
|
a.selectionSet = merge(a.selectionSet, b.selectionSet);
|
||||||
|
else if ( b.selectionSet )
|
||||||
|
a.selectionSet = b.selectionSet;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
|
||||||
|
SelectionSet: (a, b) => {
|
||||||
|
if ( a.selections && b.selections )
|
||||||
|
a.selections = mergeList(a.selections, b.selections);
|
||||||
|
else if ( b.selections )
|
||||||
|
a.selections = b.selections;
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function mergeList(a, b) {
|
||||||
|
const a_names = {};
|
||||||
|
for(const item of a) {
|
||||||
|
if ( ! item || ! item.name || item.name.kind !== 'Name' )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
a_names[item.name.value] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const item of b) {
|
||||||
|
if ( ! item || ! item.name || item.name.kind !== 'Name' )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const name = item.name.value,
|
||||||
|
idx = a_names[name] ? a.indexOf(a_names[name]) : -1;
|
||||||
|
|
||||||
|
if ( idx !== -1 )
|
||||||
|
a[idx] = merge(a[idx], item);
|
||||||
|
else
|
||||||
|
a.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function merge(a, b) {
|
||||||
|
if ( a.kind !== b.kind )
|
||||||
|
return a;
|
||||||
|
|
||||||
|
if ( MERGE_METHODS[a.kind] )
|
||||||
|
return MERGE_METHODS[a.kind](a, b);
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue