2019-06-14 21:24:48 -04:00
'use strict' ;
// ============================================================================
// Twitch Data
// Get data, from Twitch.
// ============================================================================
import Module from 'utilities/module' ;
2021-04-01 12:05:29 -04:00
import { get , debounce } from 'utilities/object' ;
2019-06-14 21:24:48 -04:00
const LANGUAGE _MATCHER = /^auto___lang_(\w+)$/ ;
2020-05-28 03:53:15 +08:00
/ * *
* TwitchData is a container for getting different types of Twitch data
* @ class TwitchData
* @ extends Module
* /
2019-06-14 21:24:48 -04:00
export default class TwitchData extends Module {
constructor ( ... args ) {
super ( ... args ) ;
2019-08-09 14:24:26 -04:00
this . site = this . parent ;
2019-06-14 21:24:48 -04:00
this . inject ( 'site.apollo' ) ;
2021-05-07 18:22:55 -04:00
this . _waiting _user _ids = new Map ;
this . _waiting _user _logins = new Map ;
2019-06-15 03:58:06 -04:00
this . _waiting _stream _ids = new Map ;
this . _waiting _stream _logins = new Map ;
2019-06-14 21:24:48 -04:00
this . tag _cache = new Map ;
this . _waiting _tags = new Map ;
2019-06-15 03:58:06 -04:00
this . _loadTags = debounce ( this . _loadTags , 50 ) ;
this . _loadStreams = debounce ( this . _loadStreams , 50 ) ;
2019-06-14 21:24:48 -04:00
}
queryApollo ( query , variables , options ) {
let thing ;
if ( ! variables && ! options && query . query )
thing = query ;
else {
thing = {
query ,
variables
} ;
if ( options )
thing = Object . assign ( thing , options ) ;
}
return this . apollo . client . query ( thing ) ;
}
2019-12-31 17:44:36 -05:00
mutate ( mutation , variables , options ) {
let thing ;
if ( ! variables && ! options && mutation . mutation )
thing = mutation ;
else {
thing = {
mutation ,
variables
} ;
if ( options )
thing = Object . assign ( thing , options ) ;
}
return this . apollo . client . mutate ( thing ) ;
}
2019-06-14 21:24:48 -04:00
get languageCode ( ) {
const session = this . site . getSession ( ) ;
return session && session . languageCode || 'en'
}
get locale ( ) {
const session = this . site . getSession ( ) ;
return session && session . locale || 'en-US'
}
2021-02-22 20:11:35 -05:00
// ========================================================================
// Badges
// ========================================================================
async getBadges ( ) {
const data = await this . queryApollo (
await import ( /* webpackChunkName: 'queries' */ './data/global-badges.gql' )
) ;
return get ( 'data.badges' , data ) ;
}
2019-06-14 21:24:48 -04:00
// ========================================================================
// Categories
// ========================================================================
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for categories matching the search argument
* @ function getMatchingCategories
* @ memberof TwitchData
* @ async
*
* @ param { string } query - query text to match to a category
* @ returns { Object } a collection of matches for the query string
*
* @ example
*
* console . log ( this . twitch _data . getMatchingCategories ( "siege" ) ) ;
* /
2019-06-14 21:24:48 -04:00
async getMatchingCategories ( query ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/search-category.gql' ) ,
2019-06-14 21:24:48 -04:00
{ query }
) ;
return {
cursor : get ( 'data.searchFor.games.cursor' , data ) ,
items : get ( 'data.searchFor.games.items' , data ) || [ ] ,
finished : ! get ( 'data.searchFor.games.pageInfo.hasNextPage' , data )
} ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for category details given the id or name . One of ( id , name ) MUST be specified
* @ function getCategory
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the category id number ( can be an integer string )
* @ param { string | null | undefined } name - the category name
* @ returns { Object } information about the requested stream
*
* @ example
*
* console . log ( this . twitch _data . getCategory ( null , 'Just Chatting' ) ) ;
* /
2019-10-22 18:32:56 -04:00
async getCategory ( id , name ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/category-fetch.gql' ) ,
2019-10-22 18:32:56 -04:00
{ id , name }
) ;
return get ( 'data.game' , data ) ;
}
2019-06-14 21:24:48 -04:00
// ========================================================================
// Users
// ========================================================================
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for users matching the search argument
* @ function getMatchingUsers
* @ memberof TwitchData
* @ async
*
* @ param { string } query - query text to match to a username
* @ returns { Object } a collection of matches for the query string
*
* @ example
*
* console . log ( this . twitch _data . getMatchingUsers ( "ninja" ) ) ;
* /
2019-06-14 21:24:48 -04:00
async getMatchingUsers ( query ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/search-user.gql' ) ,
2019-06-14 21:24:48 -04:00
{ query }
) ;
return {
cursor : get ( 'data.searchFor.users.cursor' , data ) ,
items : get ( 'data.searchFor.users.items' , data ) || [ ] ,
finished : ! get ( 'data.searchFor.users.pageInfo.hasNextPage' , data )
} ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for user details given the id or name . One of ( id , login ) MUST be specified
* @ function getUser
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the user id number ( can be an integer string )
* @ param { string | null | undefined } login - the username
* @ returns { Object } information about the requested user
*
* @ example
*
* console . log ( this . twitch _data . getUser ( 19571641 , null ) ) ;
* /
2019-06-14 21:24:48 -04:00
async getUser ( id , login ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/user-fetch.gql' ) ,
2019-06-14 21:24:48 -04:00
{ id , login }
) ;
return get ( 'data.user' , data ) ;
}
2020-09-15 19:17:29 -04:00
/ * *
* Queries Apollo for the user ' s current game , details given the user id or name . One of ( id , login ) MUST be specified
* @ function getUserGame
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the user id number ( can be an integer string )
* @ param { string | null | undefined } login - the username
* @ returns { Object } information about the requested user
*
* @ example
*
* console . log ( this . twitch _data . getUserGame ( 19571641 , null ) ) ;
* /
async getUserGame ( id , login ) {
const data = await this . queryApollo (
await import ( /* webpackChunkName: 'queries' */ './data/user-game.gql' ) ,
{ id , login }
) ;
return get ( 'data.user.broadcastSettings.game' , data ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for the logged in user ' s relationship to the channel with given the id or name . One of ( id , login ) MUST be specified
* @ function getUserSelf
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the channel id number ( can be an integer string )
* @ param { string | null | undefined } login - the channel username
* @ returns { Object } information about your status in the channel
*
* @ example
*
* console . log ( this . twitch _data . getUserSelf ( null , "ninja" ) ) ;
* /
2020-01-11 17:13:56 -05:00
async getUserSelf ( id , login ) {
const data = await this . queryApollo (
await import ( /* webpackChunkName: 'queries' */ './data/user-self.gql' ) ,
{ id , login }
) ;
return get ( 'data.user.self' , data ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for the requested user ' s latest broadcast . One of ( id , login ) MUST be specified
* @ function getLastBroadcast
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the channel id number ( can be an integer string )
* @ param { string | null | undefined } login - the channel username
* @ returns { Object } information about the requested user ' s latest broadcast
*
* @ example
*
* console . log ( this . twitch _data . getLastBroadcast ( 19571641 , null ) ) ;
* /
2019-12-05 23:13:27 -05:00
async getLastBroadcast ( id , login ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/last-broadcast.gql' ) ,
2019-12-05 23:13:27 -05:00
{ id , login }
) ;
return get ( 'data.user.lastBroadcast' , data ) ;
}
2019-06-14 21:24:48 -04:00
2021-04-01 12:05:29 -04:00
2021-05-07 18:22:55 -04:00
/ * *
* Fetch basic information on a user from Twitch . This is automatically batched
* for performance , but not directly cached . Either an id or login must be provided .
*
* @ param { Number | String } [ id ] The ID of the channel
* @ param { String } [ login ] The username of the channel
*
* @ returns { Promise } A basic user object .
* /
getUserBasic ( id , login ) {
return new Promise ( ( s , f ) => {
if ( id ) {
if ( this . _waiting _user _ids . has ( id ) )
this . _waiting _user _ids . get ( id ) . push ( [ s , f ] ) ;
else
this . _waiting _user _ids . set ( id , [ [ s , f ] ] ) ;
} else if ( login ) {
if ( this . _waiting _user _logins . has ( login ) )
this . _waiting _user _logins . get ( login ) . push ( [ s , f ] ) ;
else
this . _waiting _user _logins . set ( login , [ [ s , f ] ] ) ;
} else
f ( 'id and login cannot both be null' ) ;
if ( ! this . _loading _users )
this . _loadUsers ( ) ;
} )
}
async _loadUsers ( ) {
if ( this . _loading _users )
return ;
this . _loading _users = true ;
// Get the first 50... things.
const ids = [ ... this . _waiting _user _ids . keys ( ) ] . slice ( 0 , 50 ) ,
remaining = 50 - ids . length ,
logins = remaining > 0 ? [ ... this . _waiting _user _logins . keys ( ) ] . slice ( 0 , remaining ) : [ ] ;
let nodes ;
try {
const data = await this . queryApollo ( {
query : await import ( /* webpackChunkName: 'queries' */ './data/user-bulk.gql' ) ,
variables : {
ids : ids . length ? ids : null ,
logins : logins . length ? logins : null
}
} ) ;
nodes = get ( 'data.users' , data ) ;
} catch ( err ) {
for ( const id of ids ) {
const promises = this . _waiting _user _ids . get ( id ) ;
this . _waiting _user _ids . delete ( id ) ;
for ( const pair of promises )
pair [ 1 ] ( err ) ;
}
for ( const login of logins ) {
const promises = this . _waiting _user _logins . get ( login ) ;
this . _waiting _user _logins . delete ( login ) ;
for ( const pair of promises )
pair [ 1 ] ( err ) ;
}
return ;
}
const id _set = new Set ( ids ) ,
login _set = new Set ( logins ) ;
if ( Array . isArray ( nodes ) )
for ( const node of nodes ) {
if ( ! node || ! node . id )
continue ;
id _set . delete ( node . id ) ;
login _set . delete ( node . login ) ;
let promises = this . _waiting _user _ids . get ( node . id ) ;
if ( promises ) {
this . _waiting _user _ids . delete ( node . id ) ;
for ( const pair of promises )
pair [ 0 ] ( node ) ;
}
promises = this . _waiting _user _logins . get ( node . login ) ;
if ( promises ) {
this . _waiting _user _logins . delete ( node . login ) ;
for ( const pair of promises )
pair [ 0 ] ( node ) ;
}
}
for ( const id of id _set ) {
const promises = this . _waiting _user _ids . get ( id ) ;
if ( promises ) {
this . _waiting _user _ids . delete ( id ) ;
for ( const pair of promises )
pair [ 0 ] ( null ) ;
}
}
for ( const login of login _set ) {
const promises = this . _waiting _user _logins . get ( login ) ;
if ( promises ) {
this . _waiting _user _logins . delete ( login ) ;
for ( const pair of promises )
pair [ 0 ] ( null ) ;
}
}
this . _loading _users = false ;
if ( this . _waiting _user _ids . size || this . _waiting _user _logins . size )
this . _loadUsers ( ) ;
}
2019-10-18 20:56:33 -04:00
// ========================================================================
// Broadcast ID
// ========================================================================
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for the ID of the specified user ' s current broadcast . This ID will become the VOD ID . One of ( id , login ) MUST be specified
* @ function getBroadcastID
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } id - the channel id number ( can be an integer string )
* @ param { string | null | undefined } login - the channel username
* @ returns { Object } information about the current broadcast
*
* @ example
*
* console . log ( this . twitch _data . getBroadcastID ( null , "ninja" ) ) ;
* /
2019-10-18 20:56:33 -04:00
async getBroadcastID ( id , login ) {
const data = await this . queryApollo ( {
2019-12-31 17:44:36 -05:00
query : await import ( /* webpackChunkName: 'queries' */ './data/broadcast-id.gql' ) ,
2019-10-18 20:56:33 -04:00
variables : {
id ,
login
}
} ) ;
return get ( 'data.user.stream.archiveVideo.id' , data ) ;
}
2020-06-23 17:17:00 -04:00
async getChannelColor ( id , login ) {
const data = await this . queryApollo ( {
query : await import ( /* webpackChunkName: 'queries' */ './data/user-color.gql' ) ,
variables : {
id ,
login
}
} ) ;
return get ( 'data.user.primaryColorHex' , data ) ;
}
2019-12-31 17:44:36 -05:00
// ========================================================================
// Polls
// ========================================================================
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for information about the specified poll .
* @ function getPoll
* @ memberof TwitchData
* @ async
*
* @ param { int | string } poll _id - the poll id number ( can be an integer string )
* @ returns { Object } information about the specified poll
*
* @ example
*
* console . log ( this . twitch _data . getPoll ( 1337 ) ) ;
* /
2019-12-31 17:44:36 -05:00
async getPoll ( poll _id ) {
const data = await this . queryApollo ( {
query : await import ( /* webpackChunkName: 'queries' */ './data/poll-get.gql' ) ,
variables : {
id : poll _id
}
} ) ;
return get ( 'data.poll' , data ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Create a new poll
* @ function createPoll
* @ memberof TwitchData
* @ async
*
* @ param { int | string } channel _id - the channel id number ( can be an integer string )
* @ param { string } title - the poll title
* @ param { string [ ] } choices - an array of poll choices
* @ param { Object } [ options ] - an object containing poll options
* @ param { int } [ options . bits = 0 ] - how many bits it costs to vote
* @ param { int } [ options . duration = 60 ] - how long the poll will be held for , in seconds
* @ param { bool } [ options . subscriberMultiplier = false ] - whether to activate subsriber 2 x multiplier
* @ param { bool } [ options . subscriberOnly = false ] - whether only subscribers may vote
* @ returns { Object } poll data
*
* @ example
*
* console . log ( this . twitch _data . createPoll ( 19571641 , "Pick an option:" , [ "One" , "Two" , "Three" ] , { bits : 10 , duration : 120 , subscriberMultiplier : false , subscriberOnly : true } ) ) ;
* /
2019-12-31 17:44:36 -05:00
async createPoll ( channel _id , title , choices , options = { } ) {
if ( typeof title !== 'string' )
throw new TypeError ( 'title must be string' ) ;
if ( ! Array . isArray ( choices ) || choices . some ( x => typeof x !== 'string' ) )
throw new TypeError ( 'choices must be array of strings' ) ;
let bits = options . bits || 0 ,
duration = options . duration || 60 ;
if ( typeof bits !== 'number' || bits < 0 )
bits = 0 ;
if ( typeof duration !== 'number' || duration < 0 )
duration = 60 ;
const data = await this . mutate ( {
mutation : await import ( /* webpackChunkName: 'queries' */ './data/poll-create.gql' ) ,
variables : {
input : {
bitsCost : bits ,
bitsVoting : bits > 0 ,
choices : choices . map ( x => ( { title : x } ) ) ,
durationSeconds : duration ,
ownedBy : ` ${ channel _id } ` ,
subscriberMultiplier : options . subscriberMultiplier || false ,
subscriberOnly : options . subscriberOnly || false ,
title
}
}
} ) ;
return get ( 'data.createPoll.poll' , data ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Place specified poll into archive
* @ function archivePoll
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } poll _id - the poll id number ( can be an integer string )
* @ returns { Object } information about the specified poll
*
* @ example
*
* console . log ( this . twitch _data . archivePoll ( 1337 ) ) ;
* /
2019-12-31 17:44:36 -05:00
async archivePoll ( poll _id ) {
const data = await this . mutate ( {
mutation : await import ( /* webpackChunkName: 'queries' */ './data/poll-archive.gql' ) ,
variables : {
id : poll _id
}
} ) ;
return get ( 'data.archivePoll.poll' , data ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Terminate specified poll
* @ function terminatePoll
* @ memberof TwitchData
* @ async
*
* @ param { int | string | null | undefined } poll _id - the poll id number ( can be an integer string )
* @ returns { Object } information about the specified poll
*
* @ example
*
* console . log ( this . twitch _data . archivePoll ( 1337 ) ) ;
* /
2019-12-31 17:44:36 -05:00
async terminatePoll ( poll _id ) {
const data = await this . mutate ( {
mutation : await import ( /* webpackChunkName: 'queries' */ './data/poll-terminate.gql' ) ,
variables : {
id : poll _id
}
} ) ;
return get ( 'data.terminatePoll.poll' , data ) ;
}
2019-06-15 03:58:06 -04:00
// ========================================================================
// Stream Up-Type (Uptime and Type, for Directory Purposes)
// ========================================================================
2020-05-28 03:53:15 +08:00
/ * *
* Queries Apollo for stream metadata . One of ( id , login ) MUST be specified
* @ function getStreamMeta
* @ memberof TwitchData
*
* @ param { int | string | null | undefined } id - the channel id number ( can be an integer string )
* @ param { string | null | undefined } login - the channel name
* @ returns { Promise } information about the requested stream
*
* @ example
*
* this . twitch _data . getStreamMeta ( 19571641 , null ) . then ( function ( returnObj ) { console . log ( returnObj ) ; } ) ;
* /
2019-06-15 03:58:06 -04:00
getStreamMeta ( id , login ) {
2019-06-19 20:57:14 -04:00
return new Promise ( ( s , f ) => {
2019-06-15 03:58:06 -04:00
if ( id ) {
if ( this . _waiting _stream _ids . has ( id ) )
this . _waiting _stream _ids . get ( id ) . push ( [ s , f ] ) ;
else
this . _waiting _stream _ids . set ( id , [ [ s , f ] ] ) ;
} else if ( login ) {
if ( this . _waiting _stream _logins . has ( login ) )
this . _waiting _stream _logins . get ( login ) . push ( [ s , f ] ) ;
else
this . _waiting _stream _logins . set ( login , [ [ s , f ] ] ) ;
} else
f ( 'id and login cannot both be null' ) ;
if ( ! this . _loading _streams )
this . _loadStreams ( ) ;
} )
}
async _loadStreams ( ) {
if ( this . _loading _streams )
return ;
this . _loading _streams = true ;
// Get the first 50... things.
const ids = [ ... this . _waiting _stream _ids . keys ( ) ] . slice ( 0 , 50 ) ,
remaining = 50 - ids . length ,
logins = remaining > 0 ? [ ... this . _waiting _stream _logins . keys ( ) ] . slice ( 0 , remaining ) : [ ] ;
let nodes ;
try {
const data = await this . queryApollo ( {
2019-12-31 17:44:36 -05:00
query : await import ( /* webpackChunkName: 'queries' */ './data/stream-fetch.gql' ) ,
2019-06-15 03:58:06 -04:00
variables : {
ids : ids . length ? ids : null ,
logins : logins . length ? logins : null
}
} ) ;
nodes = get ( 'data.users' , data ) ;
} catch ( err ) {
for ( const id of ids ) {
const promises = this . _waiting _stream _ids . get ( id ) ;
this . _waiting _stream _ids . delete ( id ) ;
for ( const pair of promises )
pair [ 1 ] ( err ) ;
}
for ( const login of logins ) {
const promises = this . _waiting _stream _logins . get ( login ) ;
this . _waiting _stream _logins . delete ( login ) ;
for ( const pair of promises )
pair [ 1 ] ( err ) ;
}
return ;
}
const id _set = new Set ( ids ) ,
login _set = new Set ( logins ) ;
if ( Array . isArray ( nodes ) )
for ( const node of nodes ) {
if ( ! node || ! node . id )
continue ;
id _set . delete ( node . id ) ;
login _set . delete ( node . login ) ;
let promises = this . _waiting _stream _ids . get ( node . id ) ;
if ( promises ) {
this . _waiting _stream _ids . delete ( node . id ) ;
for ( const pair of promises )
pair [ 0 ] ( node . stream ) ;
}
promises = this . _waiting _stream _logins . get ( node . login ) ;
if ( promises ) {
this . _waiting _stream _logins . delete ( node . login ) ;
for ( const pair of promises )
pair [ 0 ] ( node . stream ) ;
}
}
for ( const id of id _set ) {
const promises = this . _waiting _stream _ids . get ( id ) ;
if ( promises ) {
this . _waiting _stream _ids . delete ( id ) ;
for ( const pair of promises )
pair [ 0 ] ( null ) ;
}
}
for ( const login of login _set ) {
const promises = this . _waiting _stream _logins . get ( login ) ;
if ( promises ) {
this . _waiting _stream _logins . delete ( login ) ;
for ( const pair of promises )
pair [ 0 ] ( null ) ;
}
}
this . _loading _streams = false ;
if ( this . _waiting _stream _ids . size || this . _waiting _stream _logins . size )
this . _loadStreams ( ) ;
}
2019-06-14 21:24:48 -04:00
// ========================================================================
// Tags
// ========================================================================
2019-06-15 03:58:06 -04:00
memorizeTag ( node , dispatch = true ) {
// We want properly formed tags.
if ( ! node || ! node . id || ! node . tagName || ! node . localizedName )
return ;
2021-04-01 12:05:29 -04:00
let tag = this . tag _cache . get ( node . id ) ;
if ( ! tag ) {
const match = node . isLanguageTag && LANGUAGE _MATCHER . exec ( node . tagName ) ,
lang = match && match [ 1 ] || null ;
tag = {
id : node . id ,
value : node . id ,
is _auto : node . isAutomated ,
is _language : node . isLanguageTag ,
language : lang ,
name : node . tagName ,
scope : node . scope
} ;
2019-06-15 03:58:06 -04:00
2021-04-01 12:05:29 -04:00
this . tag _cache . set ( node . id , tag ) ;
}
2019-06-15 03:58:06 -04:00
2021-04-01 12:05:29 -04:00
if ( node . localizedName )
tag . label = node . localizedName ;
2019-06-15 03:58:06 -04:00
if ( node . localizedDescription )
2021-04-01 12:05:29 -04:00
tag . description = node . localizedDescription ;
2019-06-15 03:58:06 -04:00
if ( dispatch && tag . description && this . _waiting _tags . has ( tag . id ) ) {
const promises = this . _waiting _tags . get ( tag . id ) ;
this . _waiting _tags . delete ( tag . id ) ;
for ( const pair of promises )
pair [ 0 ] ( tag ) ;
}
return tag ;
}
2019-06-14 21:24:48 -04:00
async _loadTags ( ) {
if ( this . _loading _tags )
return ;
this . _loading _tags = true ;
2019-06-15 03:58:06 -04:00
// Get the first 50 tags.
const ids = [ ... this . _waiting _tags . keys ( ) ] . slice ( 0 , 50 ) ;
let nodes
2019-06-14 21:24:48 -04:00
try {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/tags-fetch.gql' ) ,
2019-06-14 21:24:48 -04:00
{
2019-06-15 03:58:06 -04:00
ids
2019-06-14 21:24:48 -04:00
}
) ;
2019-06-15 03:58:06 -04:00
nodes = get ( 'data.contentTags' , data ) ;
2019-06-14 21:24:48 -04:00
} catch ( err ) {
2019-06-15 03:58:06 -04:00
for ( const id of ids ) {
const promises = this . _waiting _tags . get ( id ) ;
this . _waiting _tags . delete ( id ) ;
2019-06-14 21:24:48 -04:00
for ( const pair of promises )
pair [ 1 ] ( err ) ;
2019-06-15 03:58:06 -04:00
}
return ;
}
const id _set = new Set ( ids ) ;
if ( Array . isArray ( nodes ) )
for ( const node of nodes ) {
const tag = this . memorizeTag ( node , false ) ,
promises = this . _waiting _tags . get ( tag . id ) ;
this . _waiting _tags . delete ( tag . id ) ;
id _set . delete ( tag . id ) ;
if ( promises )
for ( const pair of promises )
pair [ 0 ] ( tag ) ;
}
for ( const id of id _set ) {
const promises = this . _waiting _tags . get ( id ) ;
this . _waiting _tags . delete ( id ) ;
for ( const pair of promises )
pair [ 0 ] ( null ) ;
2019-06-14 21:24:48 -04:00
}
this . _loading _tags = false ;
if ( this . _waiting _tags . size )
this . _loadTags ( ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries Apollo for tag information
* @ function getTag
* @ memberof TwitchData
*
* @ param { int | string } id - the tag id
* @ param { bool } [ want _description = false ] - whether the description is also required
* @ returns { Promise } tag information
*
* @ example
*
* this . twitch _data . getTag ( 50 ) . then ( function ( returnObj ) { console . log ( returnObj ) ; } ) ;
* /
2019-06-14 21:24:48 -04:00
getTag ( id , want _description = false ) {
2019-06-15 03:58:06 -04:00
// Make sure we weren't accidentally handed a tag object.
if ( id && id . id )
id = id . id ;
2019-06-14 21:24:48 -04:00
if ( this . tag _cache . has ( id ) ) {
const out = this . tag _cache . get ( id ) ;
if ( out && ( out . description || ! want _description ) )
return Promise . resolve ( out ) ;
}
return new Promise ( ( s , f ) => {
if ( this . _waiting _tags . has ( id ) )
this . _waiting _tags . get ( id ) . push ( [ s , f ] ) ;
else {
this . _waiting _tags . set ( id , [ [ s , f ] ] ) ;
if ( ! this . _loading _tags )
this . _loadTags ( ) ;
}
} ) ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries the tag cache for tag information , queries Apollo on cache miss
* @ function getTagImmediate
* @ memberof TwitchData
*
* @ param { int | string } id - the tag id
* @ param { getTagImmediateCallback } callback - callback function for use when requested tag information is not cached
* @ param { bool } [ want _description = false ] - whether the tag description is required
* @ returns { Object | null } tag information object , or on null , expect callback
*
* @ example
*
* console . log ( this . twitch _data . getTagImmediate ( 50 ) ) ;
* /
2019-06-14 21:24:48 -04:00
getTagImmediate ( id , callback , want _description = false ) {
2019-06-15 03:58:06 -04:00
// Make sure we weren't accidentally handed a tag object.
if ( id && id . id )
id = id . id ;
2019-06-14 21:24:48 -04:00
let out = null ;
if ( this . tag _cache . has ( id ) )
out = this . tag _cache . get ( id ) ;
2019-06-17 15:32:38 -04:00
if ( ( want _description && ( ! out || ! out . description ) ) || ( ! out && callback ) ) {
const promise = this . getTag ( id , want _description ) ;
if ( callback )
promise . then ( tag => callback ( id , tag ) ) . catch ( err => callback ( id , null , err ) ) ;
}
2019-06-14 21:24:48 -04:00
return out ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Callback function used when getTagImmediate experiences a cache miss
* @ callback getTagImmediateCallback
* @ param { int } tag _id - The tag ID number
* @ param { Object } tag _object - the object containing tag data
* @ param { Object } [ error _object ] - returned error information on tag data fetch failure
* /
2020-05-28 03:53:15 +08:00
2020-06-23 17:17:00 -04:00
/ * *
* Get top [ n ] tags
* @ function getTopTags
* @ memberof TwitchData
* @ async
*
* @ param { int | string } limit = 50 - the number of tags to return ( can be an integer string )
* @ returns { string [ ] } an array containing the top tags up to the limit requested
*
* @ example
*
* console . log ( this . twitch _data . getTopTags ( 20 ) ) ;
* /
2019-06-14 21:24:48 -04:00
async getTopTags ( limit = 50 ) {
const data = await this . queryApollo (
2019-12-31 17:44:36 -05:00
await import ( /* webpackChunkName: 'queries' */ './data/tags-top.gql' ) ,
2019-06-14 21:24:48 -04:00
{ limit }
) ;
const nodes = get ( 'data.topTags' , data ) ;
if ( ! Array . isArray ( nodes ) )
return [ ] ;
const out = [ ] , seen = new Set ;
for ( const node of nodes ) {
if ( ! node || seen . has ( node . id ) )
continue ;
seen . add ( node . id ) ;
2019-06-15 03:58:06 -04:00
out . push ( this . memorizeTag ( node ) ) ;
2019-06-14 21:24:48 -04:00
}
return out ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Queries tag languages
* @ function getLanguagesFromTags
* @ memberof TwitchData
*
* @ param { int [ ] } tags - an array of tag IDs
* @ returns { string [ ] } tag information
*
* @ example
*
* console . log ( this . twitch _data . getLanguagesFromTags ( [ 50 , 53 , 58 , 84 ] ) ) ;
* /
2020-05-28 03:53:15 +08:00
getLanguagesFromTags ( tags , callback ) { // TODO: actually use the callback
2019-06-14 21:24:48 -04:00
const out = [ ] ,
fn = callback ? debounce ( ( ) => {
this . getLanguagesFromTags ( tags , callback ) ;
} , 16 ) : null
if ( Array . isArray ( tags ) )
for ( const tag _id of tags ) {
const tag = this . getTagImmediate ( tag _id , fn ) ;
if ( tag && tag . is _language ) {
const match = LANGUAGE _MATCHER . exec ( tag . name ) ;
if ( match )
out . push ( match [ 1 ] ) ;
}
}
return out ;
}
2020-06-23 17:17:00 -04:00
/ * *
* Search tags
* @ function getMatchingTags
* @ memberof TwitchData
* @ async
*
* @ param { string } query - the search string
2021-04-01 12:05:29 -04:00
* @ param { string } [ locale ] - UNUSED . the locale to return tags from
2020-06-23 17:17:00 -04:00
* @ param { string } [ category = null ] - the category to return tags from
* @ returns { string [ ] } an array containing tags that match the query string
*
* @ example
*
2021-04-01 12:05:29 -04:00
* console . log ( await this . twitch _data . getMatchingTags ( "Rainbo" ) ) ;
2020-06-23 17:17:00 -04:00
* /
2019-06-15 03:58:06 -04:00
async getMatchingTags ( query , locale , category = null ) {
2021-04-01 12:05:29 -04:00
/ * i f ( ! l o c a l e )
locale = this . locale ; * /
2019-06-14 21:24:48 -04:00
2021-04-01 12:05:29 -04:00
const data = await this . queryApollo ( {
query : await import ( /* webpackChunkName: 'queries' */ './data/search-tags.gql' ) ,
variables : {
query ,
categoryID : category || null ,
limit : 100
}
} ) ;
2019-06-14 21:24:48 -04:00
2021-04-01 12:05:29 -04:00
const nodes = data ? . data ? . searchLiveTags ;
if ( ! Array . isArray ( nodes ) || ! nodes . length )
2019-06-14 21:24:48 -04:00
return [ ] ;
2021-04-01 12:05:29 -04:00
const out = [ ] ;
2019-06-14 21:24:48 -04:00
for ( const node of nodes ) {
2021-04-01 12:05:29 -04:00
const tag = this . memorizeTag ( node ) ;
if ( tag )
2019-06-15 03:58:06 -04:00
out . push ( tag ) ;
2019-06-14 21:24:48 -04:00
}
return out ;
}
2020-05-28 03:53:15 +08:00
}