Skip to content

Instantly share code, notes, and snippets.

@charlesdemers
Created May 4, 2018 19:11
Show Gist options
  • Save charlesdemers/fc4ff60287f3a099e4cef9d89bbd5476 to your computer and use it in GitHub Desktop.
Save charlesdemers/fc4ff60287f3a099e4cef9d89bbd5476 to your computer and use it in GitHub Desktop.
// Vendor
import EmberObject, {computed} from '@ember/object';
import {assert} from '@ember/debug';
import {task, timeout} from 'ember-concurrency';
const WatchQuery = EmberObject.extend({
_hostObject: null,
_query: null,
_variablesProperty: null,
_originalFetchPolicy: null,
_shouldFetchResultsProperty: null,
_subscriptions: computed(() => []),
_internalSubscription: null,
init() {
this._setupInternalSubscription();
this._addObservers();
this._setupObjectAutoDestruction();
},
setVariables(variables, refetch = true) {
this.get('_query').setVariables(variables, refetch);
},
startPolling(pollingInterval) {
this.get('_query').startPolling(pollingInterval);
},
stopPolling() {
this.get('_query').stopPolling();
},
refetch(variables) {
this.get('_query').refetch(variables);
},
fetchMore(options) {
this.get('_query').fetchMore(options);
},
subscribe(callback) {
const subscription = this.get('_query').subscribe({
next: (response) => {
callback(response);
}
});
this.get('_subscriptions').push(subscription);
return subscription;
},
subscribeOnce(callback) {
const subscription = this.subscribe((response) => {
if (response.loading) return;
this.unsubscribe(subscription);
callback(response);
});
},
unsubscribe(subscription) {
const subscriptions = this.get('_subscriptions')
.filter((activeSubscription) => subscription !== activeSubscription);
this.set('_subscriptions', subscriptions);
subscription.unsubscribe();
},
_getOnHost(property) {
return this.get('_hostObject').get(this.get(property));
},
_shouldFetchResults() {
return !this.get('_shouldFetchResultsProperty')
|| (this.get('_shouldFetchResultsProperty') && this._getOnHost('_shouldFetchResultsProperty'));
},
_addObservers() {
const variablesProperty = this.get('_variablesProperty');
const shouldFetchResultsProperty = this.get('_shouldFetchResultsProperty');
if (shouldFetchResultsProperty) {
this.get('_hostObject').addObserver(shouldFetchResultsProperty, this, () => this.get('_respondToObservers').perform());
}
if (variablesProperty) {
this.get('_hostObject').addObserver(variablesProperty, this, () => this.get('_respondToObservers').perform());
}
},
_respondToObservers: task(function *() {
// Wait for the next run loop so that if the `variables`
// and the `shouldFetch` observers fire simutaneously,
// we run this only once
yield timeout(0);
const shouldFetchResults = this._shouldFetchResults();
if (shouldFetchResults) {
const wasPolling = this.get('_query.isCurrentlyPolling');
const pollInterval = this.get('_query.options.pollInterval');
if (wasPolling) this.stopPolling();
this.get('_query').setOptions({
variables: this._getOnHost('_variablesProperty'),
fetchPolicy: this.get('_originalFetchPolicy'),
pollInterval
});
} else {
this.get('_query').setOptions({fetchPolicy: 'standby'});
}
}).restartable(),
_setupInternalSubscription() {
if (this._shouldFetchResults()) {
this.set('response', this.get('_query').currentResult());
} else {
this.get('_query').setOptions({fetchPolicy: 'standby'});
this.set('response', {loading: false, data: null});
}
const subscription = this.subscribe((response) => {
this.set('response', response);
});
this.set('_internalSubscription', subscription);
},
_setupObjectAutoDestruction() {
const hostObject = this.get('_hostObject');
const destroyMethod = hostObject.get('isComponent')
? 'willDestroyElement'
: 'willDestroy';
const oldWillDestroy = hostObject[destroyMethod];
hostObject[destroyMethod] = () => {
this.get('_subscriptions').forEach((subscription) => subscription.unsubscribe());
this.destroy();
oldWillDestroy.call(hostObject);
};
}
});
export default ({query, variables, fetchPolicy = 'network-only', shouldFetchResults, pollInterval}) => {
return computed(function() {
assert('[apollo-watch-query] Cannot find the `apollo` service, maybe you forgot to inject it?', this.get('apollo.client'));
const computedVariables = variables ? this.get(variables) : {};
const watchQuery = this.get('apollo.client').watchQuery({
query,
fetchPolicy,
fetchResults: false,
variables: computedVariables,
pollInterval
});
return WatchQuery.create({
_hostObject: this,
_query: watchQuery,
_variablesProperty: variables,
_originalFetchPolicy: fetchPolicy,
_shouldFetchResultsProperty: shouldFetchResults
});
});
};
// Vendor
import Component from '@ember/component';
import {inject as service} from '@ember/service';
import {computed} from '@ember/object';
import {notEmpty} from '@ember/object/computed';
// Computed macros
import apolloWatchQuery from 'my-app/computed-macros/apollo-watch-query';
// GraphQL
import userQuery from 'my-app/graphql/queries/user';
export default Component.extend({
apollo: service('apollo'),
userPresenter: service('users/presenter'),
userQuery: apolloWatchQuery({
query: userQuery,
variables: 'userQueryVariables',
pollInterval: 10000,
shouldFetchResults: 'userQueryShouldFetchResults'
}),
userQueryVariables: computed('userId', function() {
return {userId: this.get('userId')};
}),
userQueryShouldFetchResults: notEmpty('userId'),
user: computed('userQuery.response.{loading,data}', function() {
if (this.get('userQuery.response.loading') return null;
return this.get('userPresenter').present(this.get('userQuery.response.data.user'));
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment