350 lines
7.7 KiB
JavaScript
350 lines
7.7 KiB
JavaScript
import omit from 'lodash.omit'
|
|
import { throttle, debounce } from './utils'
|
|
import { VUE_APOLLO_QUERY_KEYWORDS } from './consts'
|
|
|
|
class SmartApollo {
|
|
type = null
|
|
vueApolloSpecialKeys = []
|
|
|
|
constructor (vm, key, options, autostart = true) {
|
|
this.vm = vm
|
|
this.key = key
|
|
this.options = options
|
|
this._skip = false
|
|
this._watchers = []
|
|
|
|
// Query callback
|
|
if (typeof this.options.query === 'function') {
|
|
const queryCb = this.options.query.bind(this.vm)
|
|
this.options.query = queryCb()
|
|
this._watchers.push(this.vm.$watch(queryCb, query => {
|
|
this.options.query = query
|
|
this.refresh()
|
|
}))
|
|
}
|
|
|
|
if (autostart) {
|
|
this.autostart()
|
|
}
|
|
}
|
|
|
|
autostart () {
|
|
if (typeof this.options.skip === 'function') {
|
|
this._watchers.push(this.vm.$watch(this.options.skip.bind(this.vm), this.skipChanged.bind(this), {
|
|
immediate: true,
|
|
}))
|
|
} else if (!this.options.skip) {
|
|
this.start()
|
|
} else {
|
|
this._skip = true
|
|
}
|
|
}
|
|
|
|
skipChanged (value, oldValue) {
|
|
if (value !== oldValue) {
|
|
this.skip = value
|
|
}
|
|
}
|
|
|
|
get skip () {
|
|
return this._skip
|
|
}
|
|
|
|
set skip (value) {
|
|
if (value) {
|
|
this.stop()
|
|
} else {
|
|
this.start()
|
|
}
|
|
this._skip = value
|
|
}
|
|
|
|
refresh () {
|
|
if (!this._skip) {
|
|
this.stop()
|
|
this.start()
|
|
}
|
|
}
|
|
|
|
start () {
|
|
this.starting = true
|
|
if (typeof this.options.variables === 'function') {
|
|
let cb = this.executeApollo.bind(this)
|
|
cb = this.options.throttle ? throttle(cb, this.options.throttle) : cb
|
|
cb = this.options.debounce ? debounce(cb, this.options.debounce) : cb
|
|
this.unwatchVariables = this.vm.$watch(() => this.options.variables.bind(this.vm)(), cb, {
|
|
immediate: true,
|
|
})
|
|
} else {
|
|
this.executeApollo(this.options.variables)
|
|
}
|
|
}
|
|
|
|
stop () {
|
|
if (this.unwatchVariables) {
|
|
this.unwatchVariables()
|
|
this.unwatchVariables = null
|
|
}
|
|
|
|
if (this.sub) {
|
|
this.sub.unsubscribe()
|
|
this.sub = null
|
|
}
|
|
}
|
|
|
|
generateApolloOptions (variables) {
|
|
const apolloOptions = omit(this.options, this.vueApolloSpecialKeys)
|
|
apolloOptions.variables = variables
|
|
return apolloOptions
|
|
}
|
|
|
|
executeApollo (variables) {
|
|
this.starting = false
|
|
}
|
|
|
|
nextResult () {
|
|
throw new Error('Not implemented')
|
|
}
|
|
|
|
catchError (error) {
|
|
if (error.graphQLErrors && error.graphQLErrors.length !== 0) {
|
|
console.error(`GraphQL execution errors for ${this.type} '${this.key}'`)
|
|
for (let e of error.graphQLErrors) {
|
|
console.error(e)
|
|
}
|
|
} else if (error.networkError) {
|
|
console.error(`Error sending the ${this.type} '${this.key}'`, error.networkError)
|
|
} else {
|
|
console.error(`[vue-apollo] An error has occured for ${this.type} '${this.key}'`)
|
|
if (Array.isArray(error)) {
|
|
console.error(...error)
|
|
} else {
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
if (typeof this.options.error === 'function') {
|
|
this.options.error.call(this.vm, error)
|
|
}
|
|
}
|
|
|
|
destroy () {
|
|
this.stop()
|
|
for (const unwatch of this._watchers) {
|
|
unwatch()
|
|
}
|
|
}
|
|
}
|
|
|
|
export class SmartQuery extends SmartApollo {
|
|
type = 'query'
|
|
vueApolloSpecialKeys = VUE_APOLLO_QUERY_KEYWORDS
|
|
loading = false
|
|
|
|
constructor (vm, key, options, autostart = true) {
|
|
// Options object callback
|
|
while (typeof options === 'function') {
|
|
options = options.call(vm)
|
|
}
|
|
|
|
// Simple query
|
|
if (!options.query) {
|
|
const query = options
|
|
options = {
|
|
query,
|
|
}
|
|
}
|
|
|
|
super(vm, key, options, autostart)
|
|
}
|
|
|
|
stop () {
|
|
super.stop()
|
|
|
|
if (this.observer) {
|
|
this.observer.stopPolling()
|
|
this.observer = null
|
|
}
|
|
}
|
|
|
|
executeApollo (variables) {
|
|
if (this.observer) {
|
|
// Update variables
|
|
// Don't use setVariables directly or it will ignore cache
|
|
this.observer.setOptions(this.generateApolloOptions(variables))
|
|
} else {
|
|
if (this.sub) {
|
|
this.sub.unsubscribe()
|
|
}
|
|
|
|
// Create observer
|
|
this.observer = this.vm.$apollo.watchQuery(this.generateApolloOptions(variables))
|
|
|
|
// Create subscription
|
|
this.sub = this.observer.subscribe({
|
|
next: this.nextResult.bind(this),
|
|
error: this.catchError.bind(this),
|
|
})
|
|
}
|
|
|
|
this.maySetLoading()
|
|
|
|
super.executeApollo(variables)
|
|
}
|
|
|
|
maySetLoading (force = false) {
|
|
const currentResult = this.observer.currentResult()
|
|
if (force || currentResult.loading) {
|
|
if (!this.loading) {
|
|
this.applyLoadingModifier(1)
|
|
}
|
|
this.loading = true
|
|
}
|
|
}
|
|
|
|
nextResult (result) {
|
|
const { data, loading } = result
|
|
|
|
if (!loading) {
|
|
this.loadingDone()
|
|
}
|
|
|
|
if (typeof data === 'undefined') {
|
|
// No result
|
|
} else if (typeof this.options.update === 'function') {
|
|
this.vm[this.key] = this.options.update.call(this.vm, data)
|
|
} else if (data[this.key] === undefined) {
|
|
console.error(`Missing ${this.key} attribute on result`, data)
|
|
} else {
|
|
this.vm[this.key] = data[this.key]
|
|
}
|
|
|
|
if (typeof this.options.result === 'function') {
|
|
this.options.result.call(this.vm, result)
|
|
}
|
|
}
|
|
|
|
catchError (error) {
|
|
super.catchError(error)
|
|
this.loadingDone()
|
|
}
|
|
|
|
applyLoadingModifier (value) {
|
|
if (this.options.loadingKey) {
|
|
this.vm[this.options.loadingKey] += value
|
|
}
|
|
|
|
if (this.options.watchLoading) {
|
|
this.options.watchLoading.call(this.vm, value === 1, value)
|
|
}
|
|
}
|
|
|
|
loadingDone () {
|
|
if (this.loading) {
|
|
this.applyLoadingModifier(-1)
|
|
}
|
|
this.loading = false
|
|
}
|
|
|
|
fetchMore (...args) {
|
|
if (this.observer) {
|
|
this.maySetLoading(true)
|
|
return this.observer.fetchMore(...args)
|
|
}
|
|
}
|
|
|
|
subscribeToMore (...args) {
|
|
if (this.observer) {
|
|
return {
|
|
unsubscribe: this.observer.subscribeToMore(...args),
|
|
}
|
|
}
|
|
}
|
|
|
|
refetch (variables) {
|
|
variables && (this.options.variables = variables)
|
|
if (this.observer) {
|
|
const result = this.observer.refetch(variables).then((result) => {
|
|
if (!result.loading) {
|
|
this.loadingDone()
|
|
}
|
|
})
|
|
this.maySetLoading()
|
|
return result
|
|
}
|
|
}
|
|
|
|
setVariables (variables, tryFetch) {
|
|
this.options.variables = variables
|
|
if (this.observer) {
|
|
const result = this.observer.setVariables(variables, tryFetch)
|
|
this.maySetLoading()
|
|
return result
|
|
}
|
|
}
|
|
|
|
setOptions (options) {
|
|
Object.assign(this.options, options)
|
|
if (this.observer) {
|
|
const result = this.observer.setOptions(options)
|
|
this.maySetLoading()
|
|
return result
|
|
}
|
|
}
|
|
|
|
startPolling (...args) {
|
|
if (this.observer) {
|
|
return this.observer.startPolling(...args)
|
|
}
|
|
}
|
|
|
|
stopPolling (...args) {
|
|
if (this.observer) {
|
|
return this.observer.stopPolling(...args)
|
|
}
|
|
}
|
|
}
|
|
|
|
export class SmartSubscription extends SmartApollo {
|
|
type = 'subscription'
|
|
vueApolloSpecialKeys = [
|
|
'variables',
|
|
'result',
|
|
'error',
|
|
'throttle',
|
|
'debounce',
|
|
]
|
|
|
|
constructor (vm, key, options, autostart = true) {
|
|
// Options object callback
|
|
while (typeof options === 'function') {
|
|
options = options.call(vm)
|
|
}
|
|
|
|
super(vm, key, options, autostart)
|
|
}
|
|
|
|
executeApollo (variables) {
|
|
if (this.sub) {
|
|
this.sub.unsubscribe()
|
|
}
|
|
|
|
// Create observer
|
|
this.observer = this.vm.$apollo.subscribe(this.generateApolloOptions(variables))
|
|
|
|
// Create subscription
|
|
this.sub = this.observer.subscribe({
|
|
next: this.nextResult.bind(this),
|
|
error: this.catchError.bind(this),
|
|
})
|
|
|
|
super.executeApollo(variables)
|
|
}
|
|
|
|
nextResult (data) {
|
|
if (typeof this.options.result === 'function') {
|
|
this.options.result.call(this.vm, data)
|
|
}
|
|
}
|
|
}
|