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;
|
||||
|
||||
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__,
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
query {
|
||||
streams {
|
||||
edges {
|
||||
fragment browsePagePopularStreamsWithTagsEdge2 on StreamEdge {
|
||||
node {
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,18 @@
|
|||
query {
|
||||
query FollowedChannels_RENAME2 {
|
||||
currentUser {
|
||||
follows {
|
||||
edges {
|
||||
node {
|
||||
stream {
|
||||
createdAt
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
followedLiveUsers {
|
||||
nodes {
|
||||
stream {
|
||||
type
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
query {
|
||||
query FollowingHosts_CurrentUser {
|
||||
currentUser {
|
||||
followedHosts {
|
||||
nodes {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
query {
|
||||
query FollowedIndex_CurrentUser {
|
||||
currentUser {
|
||||
followedLiveUsers {
|
||||
nodes {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
query {
|
||||
query FollowingLive_CurrentUser {
|
||||
currentUser {
|
||||
followedLiveUsers {
|
||||
edges {
|
||||
|
|
|
@ -1,24 +1,9 @@
|
|||
query {
|
||||
directory {
|
||||
... on Community {
|
||||
streams {
|
||||
edges {
|
||||
__type
|
||||
}
|
||||
|
||||
fragment directoryPageGameStreamWithTagsEdge on StreamEdge {
|
||||
node {
|
||||
createdAt
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on Game {
|
||||
streams {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,71 +21,6 @@ export default class Game extends SiteModule {
|
|||
this.inject('i18n');
|
||||
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(
|
||||
'game-header',
|
||||
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', 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() {
|
||||
|
|
|
@ -46,8 +46,6 @@ export default class Directory extends SiteModule {
|
|||
this.inject(Game);
|
||||
this.inject(BrowsePopular);
|
||||
|
||||
this.apollo.registerModifier('DirectoryPage_Game', res => this.modifyStreams(res), false);
|
||||
|
||||
this.DirectoryCard = this.fine.define(
|
||||
'directory-card',
|
||||
n => n.renderTitles && n.renderIconicImage,
|
||||
|
@ -217,7 +215,7 @@ export default class Directory extends SiteModule {
|
|||
// TODO: Better query handling.
|
||||
this.apollo.ensureQuery(
|
||||
'DirectoryPage_Game',
|
||||
'data.directory.streams.edges.0.node.createdAt'
|
||||
'data.game.streams.edges.0.node.createdAt'
|
||||
);
|
||||
|
||||
//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
|
||||
if ( inst.ffz_update_timer ) {
|
||||
clearInterval(inst.ffz_update_timer);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
query {
|
||||
query RecommendedChannels {
|
||||
currentUser {
|
||||
recommendations {
|
||||
liveRecommendations {
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
// ============================================================================
|
||||
|
||||
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',
|
||||
'unable to load',
|
||||
'error internal',
|
||||
|
@ -31,7 +32,7 @@ function skip_error(err) {
|
|||
for(const m of BAD_ERRORS)
|
||||
if ( err.message.includes(m) )
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
export class GQLError extends Error {
|
||||
|
@ -212,6 +213,9 @@ export default class Apollo extends Module {
|
|||
query = query_map && query_map.get(id),
|
||||
modifiers = this.modifiers[operation];
|
||||
|
||||
|
||||
const pre_modification = deep_copy(request.query);
|
||||
|
||||
if ( modifiers ) {
|
||||
for(const mod of modifiers) {
|
||||
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.client.queryManager.queryStore.store[id] = null;
|
||||
}
|
||||
|
||||
this.log.info('Query', operation, pre_modification, request.query, request.variables);
|
||||
}
|
||||
|
||||
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